home *** CD-ROM | disk | FTP | other *** search
/ Gigarom 4 / Mac Giga-ROM 4.0 - 1993.toast / FILES / VIR / Virus_Check / VCheck.p < prev    next >
Encoding:
Text File  |  1988-04-19  |  161.3 KB  |  6,070 lines  |  [TEXT/TPAS]

  1. program VCheck;
  2.  
  3. (****************************************************************
  4. Startup System Test program by Albert Lunde, Northwestern 
  5. University Copyright © 1988 - All Rights Reserved
  6.  
  7.   See "Terms of Distribution" and "Use" in comments below:
  8.  ****************************************************************)
  9.  
  10. {$U-}   {don't use turbo pascal default units}
  11.  
  12. {$R-}   {range check off}
  13. {**R-}  {these two strings}
  14. {**R+}  {signal places where range checks are needed in debugging}
  15.  
  16. {$D+}   {debug symbols on}
  17. {$B-}   {bundle bit not set}
  18. {$S+}   {segment load on}
  19.  
  20. uses pasInout,memtypes,quickdraw,osintf,toolintf,
  21.      fixmath,packintf,SANE;
  22. const
  23.   StartVersion='1.0 Beta ';
  24.   TitleVersion=' 1.0 Beta';
  25.   checksumsaltinc=$00010001;{change this to modify the way
  26.                              checksums are computed}                             
  27.   {constants to control what drives are checked}
  28.   appleshareaccessmask=$00FF;{only go where owner-see IM Vol V}
  29.                              {set $0000 to go everywhere}
  30.   checkfloppies=false;
  31.   checknonbootdrives=true;
  32. (****************************************************************
  33.  
  34. Startup System Test program by Albert Lunde, Northwestern 
  35. University Copyright © 1988 - All Rights Reserved
  36.  
  37. This is a program to detect software viruses by checking for 
  38. changes in the contents of the active system folder, the boot 
  39. blocks and all applications on connected volumes.  It does 
  40. not prevent viruses from spreading in your system, but can 
  41. alert you to their existence. It is not designed to be 
  42. specific to particular viruses, except for warning of 
  43. "dangerous" resource types when found.
  44.  
  45. ********************************************************************
  46. ** Warning: Beta Test Version, still has intermittent bugs 4/8/88 **
  47. ** It is suggested that you run this beta version only on a       **
  48. ** system that is well backed up and preferably with the MacsBug  **
  49. ** debugger installed. Most of the time this seems to work as     **
  50. ** designed, but there were occasional errors caught by Macsbug   **
  51. ** in recent versions.                                            **
  52. ********************************************************************
  53. ** I think these were due to problems caused by overloading the   **
  54. ** SCSI driver, and I have put in a fix for this, but I am not    **
  55. ** sure the fix works 4/14/88                                     **
  56. ********************************************************************
  57. ** The newest features are those for checking all attached disk   **
  58. ** drives, not just the boot volume. I have not had time to test  **
  59. ** these as much  4/19/88                                         **
  60. ********************************************************************
  61.  
  62. Terms of Distribution:
  63.  
  64. Non-commercial distribution is encouraged, with several 
  65. conditions:
  66.  
  67. 1) You must distribute the source code if you distribute the 
  68. compiled program. (The main purpose of this is to make it 
  69. difficult for viruses to spread. Users are encouraged to 
  70. recompile from the source code, since source code cannot 
  71. carry a virus.)
  72.  
  73. 2) If you modify the source code, distribute both the 
  74. original code and the modified code and include the original 
  75. comment headers with copyright notice and remarks in both files. 
  76. List a summary of your changes after the header, and add 
  77. the word "Modified" to the two Version identifiers. 
  78. You may not attach additional restrictions to 
  79. distribution of the modified code.  If I receive useful 
  80. modifications, I may add them to my versions. (Distributing 
  81. the original source makes it clearer what has been changed 
  82. and may aid support.)
  83.  
  84. 3) You may change a copying fee not to exceed $10 or the cost 
  85. of media whichever is greater.  (The intent is to put 
  86. distribution of the original program and/or modified versions 
  87. into non-profit channels to allow wider distribution)  Normal 
  88. communications and connect charges for downloading software 
  89. are permitted.
  90.  
  91. Hardware and Software: 
  92.  
  93. Written in Turbo Pascal 1.1 for the Mac (tested on a Mac SE
  94. and a Mac II) this should run on a Mac Plus,Mac SE or Mac II. 
  95. I am not sure if a Mac 512E has enough memory.  This assumes 
  96. you have HFS and a relatively recent system so it is not 
  97. appropriate for the 128K or 512K Macs with the old 64K ROMs.
  98. The program compiles and runs with Turbo 1.0 but I haven't 
  99. tested this much.
  100.  
  101. Use:
  102.  
  103. The program expects to find an input file named 
  104. "OldSystemCheckSum" in either the default folder or the 
  105. system folder.  It will optionally write an output file 
  106. (default name "NewSystemCheckSum") in the same format as the 
  107. input file.  (Both are text files with items separated by tab 
  108. characters).  The program compares the contents of your 
  109. system folder with information in the input file, and informs 
  110. you of changes. It also does a less detailed checksum of 
  111. applications. It monitors the existence of hidden files.
  112.  
  113. Use of this program does not prevent a virus infecting your 
  114. system, but it may give you an indication that you are 
  115. infected.
  116.  
  117. When the program starts you are presented with a choice of 
  118. five buttons:
  119.  
  120. "System Only" button:
  121.  
  122.           This does a complete check of the system folder, 
  123.           and do nothing with applications and hidden files 
  124.           elsewhere.
  125.  
  126. "Application Scan" button:
  127.  
  128.           This will start a complete check of the system 
  129.           folder and a check for changes in the sizes the 
  130.           resource forks of applications. After about 10 
  131.           seconds the Mac will continue as if you had clicked 
  132.           this option. This is faster but less accurate than 
  133.           the "Full Check". Because some applications write 
  134.           preferences information to their own resource fork, 
  135.           this check produces more false alarms than the 
  136.           "Full Check".
  137.  
  138. "Full Check" button:
  139.  
  140.           This does a complete check of the system folder and 
  141.           a check for changes in resources of applications. 
  142.           Only resource types marked as known to contain 
  143.           executable code are checked in applications and 
  144.           invisible files.
  145.  
  146. "Skip It" button:
  147.  
  148.           Halt the program
  149.  
  150. "ShutDown" button:
  151.  
  152.           Flush all drives and do a system shutdown. (similar 
  153.           to the item in the Finder Special Menu).
  154.  
  155.  
  156. Key Commands: 
  157.  
  158.      {command key ignored} 
  159.      "Q"- Quit after closing files 
  160.      "F"- same as "Full Check" button 
  161.      "A"- same as "System Only" Button 
  162.      "Y"- same as Yes Button 
  163.      "N"- same as No  Button .
  164.      " "- Quit immediately 
  165.      "*"- invoke MacsBug debugger and turn on additional output. 
  166.      (don't use this command without a debugger)
  167.  
  168.  
  169. If you hold down the option key while clicking on one if the 
  170. startup buttons, you will be given an option to create an 
  171. output file.
  172.  
  173. After the program starts, at any time you may quit the 
  174. program by clicking the "Halt" button or pressing the "Q" 
  175. key. You may shut down the system with the "Shutdown" button.
  176.  
  177. To get started:
  178.  
  179. Place the compiled program anywhere outside the system folder 
  180. and run it, clicking on "FullCheck". It may be necessary to 
  181. increase the memory allowed by MultiFinder using the Get Info 
  182. dialog. (500K is reasonable).
  183.  
  184. The first time you run it, the program will not find the 
  185. input file, and will ask you if you want to specify another 
  186. input file. Click on NO. Click YES when it asks you to 
  187. specify an output file.  When the program runs, the output 
  188. file should contain a summary of resources in the system 
  189. file of applications and hidden files. This checks all 
  190. connected disk drives, optionally excluding floppies and 
  191. folders on AppleShare file servers.
  192.  
  193. Rename this file to "OldSystemCheckSum" and place it in the 
  194. system folder or in the same folder as the program.
  195.  
  196. Now, whenever you run the program, it will the file 
  197. "OldSystemCheckSum" as a standard of comparison and inform 
  198. you of changes.  If you want maximum protection, make this 
  199. program your startup application with Set Startup.
  200.  
  201. When you install new software in the system folder or make 
  202. some changes in system settings you may get messages about 
  203. new or changed resources.  You will also get messages when 
  204. you add an application, or move, rename or duplicate an 
  205. application.
  206.  
  207. These messages will continue to appear until you create an 
  208. new output file and rename it to  "OldSystemCheckSum". The 
  209. suggested way to do this is hold down the option key while 
  210. clicking of the "FullCheck" button at startup. Running 
  211. "FullCheck" whenever you write an output file gives you the 
  212. information necessary to do a complete comparison later.
  213.  
  214. To see how the warning messages look, move some small 
  215. application into the system folder, and rerun the program.
  216.  
  217. You can see what has changed either by looking  at the on-
  218. screen messages or by creating an output file and comparing 
  219. it with the old file. New or changed resources are flagged 
  220. "new??" or "changed??" in the output file. Deletions are not 
  221. marked. Applications are marked as "moved/renamed??',"new??",
  222. "changed??". Hidden files are marked as "(hidden)" if they 
  223. are not applications.
  224.  
  225. To make it more difficult to evade checksums, users are 
  226. encouraged to change the value of the constant 
  227. "checksumsaltinc" from $00010001 to some other longword hex 
  228. value containing mostly zeros, but some non-zero digits in 
  229. both the lower and upper half. Changing this value changes 
  230. the non-linearity of the checksums, and changes the results, 
  231. so that a change that would be undetected for one value might 
  232. not be for another.
  233.  
  234. More about the Checksums:
  235.  
  236. In order to reduce unnecessary messages and speed processing, 
  237. some resources and some parts of the boot blocks are excluded 
  238. from the checks. Resources types are classified as:
  239.  
  240.      0 "Safe"
  241.           (Not containing executable code)
  242.           for example: "STR#","FONT","ICON" 
  243.  
  244.      1 "Unknown"
  245.           (Not otherwise classified) 
  246.  
  247.      2 "Unsafe"
  248.           (Containing executable code)
  249.           for example:"CODE","INIT" 
  250.  
  251.      3 "Dangerous"
  252.           (Known only to occur in reported viruses)
  253.  
  254.  
  255. "Safe" resources are excluded from system folder checksums.
  256.  
  257.  
  258. Only "unsafe" resources are checked in application and hidden 
  259. files and only a file by file checksum is kept, not a 
  260. resource by resource checksum.
  261.  
  262. A basic list of resource types is in the program, and 
  263. "unknown" resources can be reclassified by changing the input 
  264. file.
  265.  
  266. There is also a list of key phrases which indicate a file in 
  267. the system folder may safely contain changes in "unknown" 
  268. resource types. If one of these keywords is found as a 
  269. substring in the filename both "safe" and "unknown" resources 
  270. are excluded from checksums. This is used to reduce 
  271. unnecessary warnings about changes in the Clipboard, 
  272. Scrapbook and settings files stored in the system folder by 
  273. applications.
  274.  
  275. A checksum of checksums is done across resource types.  This 
  276. will change when any contents of the checked resources change 
  277. or when the criteria for what is to be checked change.  This 
  278. will change when resources are deleted, while the resource by 
  279. resource lists of changes only indicate new or changed 
  280. resource.
  281.  
  282. No grand checksum is done for applications. The way that 
  283. applications are identified is by their 4 character creator 
  284. signature and creation date and time.
  285.  
  286. Hidden files are not checked for size on a short check and 
  287. they are only checked for "unsafe" resource type changes on a 
  288. full check. This is because the DeskTop and other normal 
  289. hidden files change size.
  290.  
  291. If you want to check attached floppies set the constant:
  292. "checkfloppies=true" at the start of the program.
  293. if you only want to check the boot drive set the constant:
  294. "checknonbootdrives=false".
  295.  
  296. As the program is now set It only looks at the top level of
  297. AppleShare file Servers, and does not descend into folders
  298. unless you are the owner. The "AppleshareAccessMask" constant
  299. controls this. The current setting is $00FF. Setting this
  300. to $0000 will attempt to search all folders on Appleshare servers
  301. (and very likely bring AppleTlak to its knees). 
  302. (See Inside Mac Volume V for more information. I am using the 
  303. new access information parameter returned by PBGetCatInfo.)
  304.  
  305.  
  306. Disclaimers:
  307.  
  308. I do not warrant that this software will alert you to all 
  309. viruses. (It won't.) I don't claim to be an expert in 
  310. eradicating software viruses and can not do long-distance 
  311. consulting on problems with them. I have designed this 
  312. program from general considerations rather than experience 
  313. with particular viruses.
  314.  
  315. I have taken reasonable care that this program do no harm, 
  316. but I cannot assure this.  My main consideration has been to 
  317. put something together quickly to help detect viruses and 
  318. reduce their spread. Getting this out the door in time to be 
  319. useful precludes exhaustive testing.
  320.  
  321. Northwestern University Apple Tech Support is assisting in 
  322. distributing this program, but they do not take responsiblity
  323. for its continued support.
  324.  
  325. Acknowledgements: 
  326.  
  327. Thanks to Bob Hablutzel and John Norstad for their advice and support
  328. during the development of the program.
  329.  
  330. This code owes a lot to a number of sources. My references include:
  331.  
  332. "Inside Macintosh" Volumes I to V
  333.  
  334. (A lot of use is made of the resource section and the Volume 
  335. IV parameter block file system calls}
  336.  
  337. Apple Tech Notes In particular:
  338.  
  339. 67  Finding the blessed folder 
  340. 68  Searching all Directories on an HFS Volume 
  341. 69  Setting ioFDirIndex in PBGetCatInfo Calls 
  342. 77  HFS ruminations
  343.  
  344. "MacTutor" Magazine 
  345. "Macintosh Revealed" Vol I & II by Stephen Chernicoff 
  346. "How to Write Macintosh Software" by Scott Knaster 
  347. "Macintosh Programming Secrets" by Scott Knaster 
  348. "Programming with Macintosh Programmer's Workshop" by Joel West 
  349. "Fundamentals of Data Structures" by Ellis Horowitz and Sartaj Sahni 
  350. "Programming Pearls" Jon Bentley
  351.  
  352.  
  353. Bug Reporting:
  354.  
  355.     I can be reached at:
  356.  
  357.     E-Mail
  358.  
  359.         LUNDE@NUACC.BITNET 
  360.  
  361.         LUNDE@NUACC.ACNS.NWU.EDU  (Internet)
  362.  
  363.     U.S. Mail
  364.  
  365.         Albert Lunde 
  366.         Academic Computing 
  367.         Northwestern University 
  368.         2129 Sheridan Road 
  369.         Evanston, IL 60202
  370.         
  371.     Related messages can also be sent to me care of
  372.     Northwestern University Apple Tech Support:
  373.         
  374.         A42 - AppleLink;
  375.             or
  376.         76474,154   - CompuServe   
  377.     
  378.  
  379. If you get system bombs, record the ID number and what was 
  380. happening prior to the error. 
  381. If you have Macsbug, use "wh" to see where you are in memory.  
  382. If you have any debugger, record the registers. Since this is
  383. a non-commercial effort, and I am giving out the source code,
  384. whatever you can do to localize and diagnose bugs will be 
  385. appreciated.   I do not know at this time how much time I can 
  386. or will spend on support and revisions.
  387.  
  388. Notes to Hackers:
  389.  
  390. There is room for improvement here. An assembly language 
  391. checksum function could be faster.  I suggest any checksum 
  392. method should meet a few criteria: Any one bit change should 
  393. be likely to change the checksum.  Transpositions of bytes 
  394. should change the checksum. Any checksum function should be 
  395. non-linear with respect to addition and exclusive or. 
  396.  
  397. That is, roughly speaking:
  398.  
  399. F(a xor b)  <>  F(a) xor F(b) 
  400.  
  401. F(a + b)    <>  F(a) +   F(b) 
  402.  
  403. I think my combination of shifts,xors and sums satisfies 
  404. these conditions.
  405.  
  406. The code was patched together from other projects and has 
  407. odds and ends that are unnecessary for this reason. The 
  408. checksum of applications was an afterthought that reduces the 
  409. speed, but this seemed important after the advent of viruses 
  410. that patch applications as well as the system.
  411.  
  412. I would be interested to know of conversion issues going to 
  413. other Pascal compilers. My use of Turbo's "shr" "shift 
  414. right", "xor" and other inline bit manipulation operators for 
  415. speed may cause some localized portability problems.
  416.  
  417. The program is designed over-all to make spreading viruses 
  418. more difficult, not impossible, with trade-offs relative to 
  419. speed and convenience. This is why I do a less elaborate 
  420. checksum on applications. The program does not use custom 
  421. icons or any other resources, so that it will be easier to 
  422. give out in source code form.
  423.  
  424. I am checking the contents of the resource file by reading 
  425. the resource map myself, opening the resource fork as a read-
  426. only file. An earlier version used LoadResource and 
  427. DetachResource, but this had bugs which may come from 
  428. fragmentation of the system heap when resources were 
  429. repeatedly loaded into it.
  430.  
  431. ****************************************************************)
  432.  
  433. {Development versions by Albert Lunde:}
  434.  
  435. {version 22 modified to read resource maps directly}
  436. {this version has some bugs in memory usage that are gradually
  437.  eating up free memory - I suspect open/close - and/or TE 4/1/88} 
  438. {version 23 the size of the TE handle is increasing but the text size is
  439.   stating about the same , also memory allocation is a bit out of balance}
  440. {version 24 rewrite of memory allocation to reduce the change of
  441.   stray handles in the resource reading routines- still has trouble
  442.   with the TE handle size 4/3/88}
  443. {version 25 fix new TE bug by explictly changing 
  444.      the tehandle size when it gets clearly too big 4/4/88}
  445. {version 27 hide debugging code, fix problem with ids, fix problem
  446.      with input/output '*****' flag recognition}
  447. {version 28 hidden "D" key to invoke Macsbug added 4/5/88}
  448. {version 29 added relative positioning in resource reading code to
  449.             allow buffering to work better 4/5/88}
  450. {version 30 reduce minimum memory safety factor, hide debugging
  451.           output 4/5/88}
  452. {version 31 add auto-start after delay}
  453. {version 32 cleanup for distribution, remove zero size handle check,
  454.    add more safe keys, add beep at end  4/8/88}
  455. {version 33 add a bit of debuggging and I/O checking 4/8/88}
  456. {version 34 add application size checks 4/10/88}
  457. {version 36 application checksums/detail output added but buggy 4/10/88}
  458. {version 37 fix bugs,trim blanks before file name comparisions 4/11/88}
  459. {version 38 shift creationdates 4 bits, modify startup interface
  460.             this needs more testing 4/11/88}
  461. {version 39 minor mods to application sort/compare, interface 4/12/88}
  462. {version 40 add check of invisible files 4/14/88}
  463. {version 41 add folder names to info on applications 
  464.             add delay after disk operations to
  465.             fix intermittent bug in SCSI interrupt handler 
  466.             fiddle with user interface 4/14/88}
  467. {version 42 tweak new features and revise comments 4/14/88}
  468. {version 43 add system/only option and tweak startup 4/15/88}
  469. {version 44 bug fix in version 43 mods, add more key abbreviations
  470.             change debugger invocation to "*" to avoid miskeys 4/15/88}
  471. {version 45 modify treatment of application scan output 4/16/88}
  472. {version 46 fix minor bug in % display, clean up comments 
  473.              add PREF to safe types list  4/17/88}
  474. {version 47  add to safe types list - display resource names 4/17/88}
  475. {version 50 begin adding  multi-volume checks 4/18/88}
  476. {version 51 1st version with full multi-volume checks
  477.             seems to be working - 4/18/88}
  478. {version 52 tweak multi-volume checks - 4/19/88}
  479. {release as version 1.0 beta 4/19/88}
  480.  
  481.  const
  482.     {parameters of delay to avoid overloading disk drivers}
  483.     scsi_wait_limit=8;{wait after this many disk operations}
  484.     scsi_wait_ticks=2;{wait this long}
  485.     {debugging stuff}
  486.     dbaopen=1;
  487.     dbatype=2;
  488.     dbaref=3;
  489.     dbadata=4;
  490.     dbamax=4;
  491.     
  492.  {buttons}
  493.   mbutton=9;
  494.   nodefaultbut=0;
  495.   yesbut=2;
  496.   nobut=4;
  497.   haltbut=1;
  498.   continuebut=3;
  499.   shutdownbut=5;
  500.   
  501.   skipitbut=6; 
  502.   sysonlybut=7;
  503.   shortbut=8;
  504.  fullbut=9;
  505.  
  506.     
  507.   startupdefaultbutton=shortbut;
  508.   
  509.   startupdelay=10;{seconds}
  510.   
  511.   {limits on what can be checked}
  512.   maxinfo=800;{total resources in system folder }
  513.   maxtype=200;{total resource types in system folder }
  514.   maxsysfiles=100;{total files in system folder}
  515.   maxappl=300;{total applications and hidden files}
  516.   maxvols=16;{volumes}
  517.   maxsafekeywords=20;
  518.   myfilenamesize=31;
  519.   thenamesize=10;
  520.   maxtokens=10;{input scanner limit}
  521.   floppycutoffsize=900;{size limit used to identify
  522.                         floppy drives}
  523.   {number of status lines}
  524.   mstatus=7;
  525.  {postions of status lines}
  526.   titleline=1;
  527.   byline=2;
  528.   pathline=3;
  529.   fileline=4;
  530.   errorline=5;
  531.   memline=5;
  532.   AskLine=6;
  533.   resline=6;
  534.   
  535.   {rescaling of creation dates by factor of 1/16}
  536.   creationdateshr=4;
  537.   creationdatemask=$0FFFFFFF;
  538.   
  539.   {resource match flags}
  540.   fnamemask=$3FFF;{mask off top bits of filename index}
  541.   idmatchmask=$8000;
  542.   exactmatchmask=$4000;
  543.   
  544.   {application match flags}
  545.   applrenamemask=$0100;
  546.   applexactmatchmask=$0200;
  547.   applchangedmask=$0400;
  548.   appldangermask=$0800;
  549.   applbadsizemask=$1000;
  550.   applbadcheckmask=$2000;
  551.   applinvisiblemask=$4000;{flag invisible files}
  552.   applvolumemask=$00FF;{subscript of volume}
  553.   notcounted=-9;{flag results of short check in unsafecount}
  554.   
  555.   {grow zone function guard block size:
  556.   This block is released if the heap is full and the
  557.   program stops with a warning. Reducing this would make the program
  558.   run in less memory but if it runs out it would die unpleasantly}
  559.   
  560.   GZguardblocksize=70000;
  561.   
  562.      {stuff from tech note#77}
  563.  
  564.         SysWDProcID    = $4552494B;    {“ERIK”}
  565.         BootDrive       = $210;    {address of Low-Mem global BootDrive}
  566.         FSFCBLen    = $3F6;      {address of Low-Mem global to distinguish file systems }
  567.         SysMap        = $A58;    {address of Low-Mem global that contains system map reference number}
  568.  
  569.   type
  570.       myfilenametype=string[myfilenamesize];
  571.             WordPtr = ^Integer;            {Pointer to a word(2 bytes)}
  572.  
  573.       {info on system folder resources}
  574.       resourceinforec  = record
  575.                          thesize:longint;
  576.                          thetype:restype;
  577.                          theid:integer;
  578.                          filenameindex:integer;{also flags in the high bits}
  579.                          checksum:integer;
  580.                          thename:string[thenamesize];
  581.                          end;
  582.       resourceinfoarray=array[1..maxinfo] of resourceinforec;
  583.       resourceinfoarrayptr=^resourceinfoarray;
  584.       safetype=(safe,unknown,unsafe,dangerous);
  585.       resourcetypeinforec = record
  586.                             thetype:restype;
  587.                             safety:safetype;
  588.                             occurs:integer;
  589.                             oldocurrs:integer;
  590.                             end;
  591.       resourcetypeinfoarray = array[1..maxtype] of resourcetypeinforec;
  592.       tokenstype=array[1..maxtokens] of str255;
  593.       {info on volumes}
  594.       
  595.        myvolumerec= record
  596.                       volrefnum:integer;
  597.                       vcreation:longint;
  598.                       vname:myfilenametype;{change this}
  599.                       vsize:longint;
  600.                       vindex:integer;
  601.                       isboot:boolean;
  602.                       attributes:integer;
  603.                       matchto:integer;
  604.                       checkvol:boolean;
  605.                       end;
  606.                       
  607.        myvolumearraytype=array[1..maxvols] of myvolumerec;
  608.        
  609.       {info on applications and hidden files}
  610.       applinforec=record
  611.                   thesize:longint;
  612.                   creator:OStype;
  613.                   creationdate:longint;{shifted right}
  614.                   dirid:longint;
  615.                   filename:myfilenametype;
  616.                   unsafecount:integer;
  617.                   checksum:integer;
  618.                   checksize:longint;
  619.                   flags:integer;
  620.                   end;
  621.                   
  622.       applinfoarray=array[1..maxappl] of applinforec;
  623.       applinfoarrayptr=^applinfoarray;
  624.       
  625.       {Types for directly reading resource maps}
  626.       
  627. type     
  628.              myresstatustype=(pathbad,pathempty,pathopen,
  629.                               typelistopen,reflistopen);
  630.     
  631.   
  632.              {resource map}
  633.              myresMaptype=record
  634.                                dummy1:array[1..5] of longint;
  635.                                dummy2:integer;
  636.                                res_file_attributes:integer;
  637.                                offset_map_to_typelist:integer;
  638.                                offset_map_to_namelist:integer;
  639.                                end;
  640.  
  641.              {resource type list items}
  642.              myresTypeListitemtype=record
  643.                                thetype:restype;
  644.                                count_minus_one:integer;
  645.                                offset_typelist_to_reflist:integer;
  646.                                end;
  647.  
  648.              myresTypeList=array[0..0] of myresTypeListitemtype;
  649.              myresTypeListptr=^myresTypeList;
  650.              myresTypeListhandle=^myresTypeListptr;
  651.  
  652.              {resource reference list items}
  653.              myresRefListitemtype=record
  654.                                theid:integer;
  655.                                offset_namelist_to_name:integer;
  656.                                attrib_and_offset:longint;
  657.                                dummy1:longint;
  658.                                end;
  659.                                
  660.              myresreflisttype=array[0..0] of myresRefListitemtype;
  661.              myresreflistptr=^myresreflisttype;                                       
  662.              myresreflisthandle=^myresreflistptr; 
  663.                             
  664.              {My "path" to the resource data. This includes
  665.               some redundant information and buffer space}                       
  666.              myresPathtype=record
  667.                          volref:integer;
  668.                          fileref:integer;
  669.                          filename:str255;
  670.                          {absolute offsets}
  671.                          offset_to_res_data:longint;
  672.                          offset_to_res_map:longint;
  673.                          offset_to_typelist:longint;{derived}
  674.                          offset_to_namelist:longint;{derived}
  675.                          map:myresmaptype;
  676.                          typelist:myresTypeListHandle;
  677.                          reflist:myresReflistHandle;
  678.                          resdata:handle;
  679.                          current_type:restype;
  680.                          current_type_subscript:integer;
  681.                          status:myresstatustype;{state of path}
  682.                          ntypes:integer;{number of types}
  683.                          nrefs:integer;{number of references to current type}
  684.                          end;
  685.  
  686. var
  687.     scsi_wait_count:longint;
  688.     currentvolumesubscript:integer;
  689.     myRpath:myresPathtype;
  690.     showdebuginfo:boolean;
  691.     fastapplcheck:boolean;
  692.     skipapplcheck:boolean;
  693.     dbarray:array[1..dbamax] of longint;{for debug}
  694.     notsafecount:longint;{number of resources not in a safe category}
  695.     safetynames:array[safe..dangerous] of string[10];
  696.     blessed:longint;{dir id of the blessed folder}
  697.     blessedpath:str255;{path name of blessed folder}
  698.     blessedbootvolwd:integer;
  699.     startupwd:integer;{working directory on startup}
  700.     buttons:array[1..mbutton] of controlhandle;
  701.     buttonrects:array[1..mbutton] of rect;
  702.     defaultbutton:integer;
  703.     quitting,finished:boolean;
  704.     bootblockchecksum,oldbootblockchecksum:integer;
  705.     checksumchecksum,oldchecksumchecksum:longint;
  706.     askanswer:boolean;{answer from yes,no buttons}
  707.     askanswered:boolean;{set true when button clicked}
  708.     optionkeyflag:boolean;
  709.     WriteOutputFlag:boolean;
  710.     {event handler globals}
  711.     theevent:   EventRecord;
  712.     mainwindow:Windowptr;
  713.     wbounds,textbounds,textframe:rect;
  714.     statustext:tehandle;
  715.  
  716.     rcount:longint;{count of resources checked in system folder}
  717.     acount:longint;{count of apps and hidden files}
  718.     oldvcount,vcount:integer;{count of volumes}
  719.     oldvols,newvols:myvolumearraytype;{lists of volumes checked}
  720.     
  721.     {pointers to the two big blocks of memory which are seperately 
  722.      allocated to reduced the total size of globals}
  723.     rinfo:resourceinfoarrayptr;
  724.     ainfo:applinfoarrayptr;
  725.     
  726.     rtypes:resourcetypeinfoarray;
  727.     rtypes_count:integer;
  728.     
  729.     infile,outfile:text;
  730.     inputopen,outputopen,inputnotdefault:boolean;
  731.     
  732.     line,filename,filedef : str255;
  733.     fileref,volref:integer;
  734.     blanks,procname:str255;
  735.     cancel:boolean;
  736.     currentresourcefileopen:integer;
  737.     sysfiles:array[1..maxsysfiles] of myfilenametype;
  738.     
  739.     safekeywords_count:integer;
  740.     safekeywords:array[1..maxsafekeywords] of myfilenametype;
  741.     
  742.     {Mydebug message globals}
  743.     mydebugport : grafptr;
  744.  
  745.     {growzone function stuff}
  746.     growzoneguardblock:handle;
  747.     lowmemoryGZflag:boolean;
  748.  
  749.   
  750. {forward procedure declarations, not in any
  751. particular order}
  752.  
  753. procedure debugger;inline $A9FF;{invoke macsbug}
  754. procedure debugStr(str:str255);inline $ABFF;{macsbug with string}
  755. procedure halt_on_error(err:oserr;sss:str255);forward;
  756. procedure detail_appl_check;forward;
  757. procedure show_appl_detail_changes;forward;
  758.     
  759. procedure Doevent(dontloop:boolean);forward;
  760.  
  761. { ShutDown is new, but works with all machines with new system. }
  762. PROCEDURE ShutDwnPower;
  763.     INLINE $3F3C,$0001,$A895;
  764.  
  765. procedure check_a_file(index:integer);forward;
  766. procedure checksum_all_appl;forward;
  767. procedure poststatus(ss:str255;linenum:integer);forward;
  768. procedure replaceline(ss:str255;linenum:integer);forward;
  769. procedure dobutton(whichbutton:integer);forward;
  770. procedure drawbuttons;forward;
  771. procedure showstatus;forward;
  772. procedure close_all_and_halt(beep:boolean);forward;
  773. procedure folder_name(dirid:longint;var name:str255);forward;
  774.  
  775. function Ask(question:str255;default:integer):boolean;forward;
  776. procedure wait_for_buttons(ss:str255;default:integer);forward;
  777. procedure clear_to_end(linenum:integer);forward;
  778. procedure summary;forward;
  779. procedure set_default_blessed;forward;
  780. procedure set_default_by_id(DirID:longint);forward;
  781. function checksumHdataOLD(h:handle):integer;forward;
  782. function checksumHdata(h:handle):integer;forward;
  783. function checksum_boot_blocks:integer;forward;
  784. procedure sorttypes(var X:resourcetypeinfoarray;N:integer);forward;
  785. procedure tabscan(line:str255; var tokens:tokenstype;var ntokens:integer);forward;
  786. procedure filltype(var tt:restype;ss:str255);forward;
  787. procedure open_output;forward;
  788. procedure open_input;forward;
  789. procedure get_set_blessed;forward;
  790.  
  791. procedure setupmydebug;{setup extra graphics port 
  792. for drawing direct to the screen without using the window manager}
  793. const  dbtop=260;
  794.        dbleft=40;
  795.        dbwidth=200;
  796.        dblength=50;
  797.        
  798. var    saveport:grafptr;
  799.        dbrect : rect;
  800.        begin
  801.        getport(saveport);{save current port}
  802.      mydebugport:=grafptr(NewPtr(sizeof(grafport)));
  803.      {make non-relocatable block}
  804.      openport(mydebugport); 
  805.        setorigin(-dbleft,-dbtop); 
  806.        {set new origin so (0,0) is at (dbleft,dbtop)
  807.                          on the screen }
  808.        (*debug_mess('Start Debug');*)
  809.        setport(saveport);{restore current port}
  810. end; {of proc mysetup debug}
  811. procedure debug_mess(message:str255);
  812. const  dbtop=260;
  813.        dbleft=40;
  814.        dbwidth=200;
  815.        dblength=50;
  816.        waitsec=2;
  817.        
  818. var    saveport:grafptr;
  819.        dbrect : rect;
  820.        waittick,dumm:longint;
  821. begin
  822.  
  823. {---------------------------}
  824. getport(saveport);{save current port}
  825. setport(mydebugport);{change to debug port}  
  826.        setrect(dbrect,0,0,dbwidth,dblength);
  827.        fillrect(dbrect,Dkgray); {draw a pseudo-window} {make this fancer later}
  828.        penpat(white);
  829.        framerect(dbrect);
  830.        moveto(20,20);
  831.        TextMode(Srcbic);{white letters}
  832.        DrawString(message);
  833.        waittick:=60*waitsec;
  834.        delay(waittick,dumm);
  835. setport(saveport);{restore current port}
  836. {-------------------------}
  837.  
  838. end; {of function debug_message}
  839. procedure debug_long(l:longint;tag:str255);
  840. var ss:str255;
  841. begin
  842. numtostring(l,ss);
  843. ss:=concat(tag,ss);
  844. debug_mess(ss);
  845. end;
  846.  
  847. {---memory management tools---}
  848. function GoodPointer(p:ptr;tag:str255):boolean;
  849. {check that this is a pointer to non-nil data
  850. within the application memory area}
  851. const
  852.       CurrentA5=$0904;
  853.       ApplZone=$02AA;
  854.       Lo3Bytes=$00FFFFFF;
  855. type
  856.         lp=^longint;
  857.         
  858. var     a:lp;
  859.         high,low,add:longint;
  860.         ok:boolean;
  861. begin
  862. ok:=false;
  863.    if p<>nil then
  864.      begin
  865.      add:=longint(ord4(p) and Lo3bytes);
  866.      if not odd(add)then
  867.         begin
  868.           {this test is a bit strict/mem-map dependent}
  869.           a:=pointer(ApplZone);
  870.           low:=a^;
  871.           a:=pointer(CurrentA5);
  872.           high:=a^;
  873.           if (add>=low) and (add<high) then
  874.               begin
  875.                   ok:=true;
  876.               end
  877.           else
  878.               begin
  879.                  debug_mess('Pointer outside User Memory');
  880.                 sysbeep(5);sysbeep(5);
  881.               end;
  882.         end
  883.       else
  884.         begin
  885.            debug_mess('Pointer With Odd Address');
  886.         end
  887.       ;{endif}
  888.         
  889.      end;
  890. if not ok and (p<>nil) then
  891.      begin
  892.      debug_mess(concat('GP>',tag));
  893.      repeat until button;
  894.      end;
  895. Goodpointer:=ok;
  896. end;{goodPointer}
  897. function GoodHandle(h:handle;tag:str255):boolean;        
  898. var     a:ptr;
  899.         ok:boolean;
  900.        
  901. begin
  902. ok:=false;
  903.    if GoodPointer(ptr(h),concat('GH1:',tag)) then
  904.         begin
  905.         if GoodPointer(h^,concat('GH2:',tag)) then
  906.            begin
  907.            {if GetHandleSize(h) >0 then}
  908.                begin;
  909.                   ok:=true
  910.                end
  911.            {else
  912.               begin
  913.                 debug_mess('Handle Size<0');
  914.               end}
  915.            ;{endif}
  916.            end
  917.            
  918.          else
  919.            begin
  920.               debug_mess('Handle^ is bad or nil');
  921.            end
  922.          ;{end if}
  923.          end
  924.      else
  925.         begin
  926.            if h<>nil then
  927.            debug_mess('Handle is bad');
  928.         end
  929.      ;{endif}
  930.         
  931. if not ok and (h<>nil) then
  932.     begin
  933.     debug_mess(concat('GH>',tag));
  934.     repeat until button;
  935.     end;
  936.       
  937. GoodHandle:=ok;
  938. end;{goodhandle}
  939.  
  940. procedure scsi_wait;
  941. {periodic delays to keep from overwhelming the scsi driver}
  942. var wait,endit:longint;
  943. begin
  944. scsi_wait_count:=scsi_wait_count+1;
  945. if scsi_wait_count>scsi_wait_limit then
  946.   begin
  947.     wait:=scsi_wait_ticks;
  948.     delay(wait,endit);
  949.     scsi_wait_count:=0;  
  950.   end;
  951. end;
  952.  
  953. procedure read_input(var line:str255);
  954. {readln(infile,line) with delay added}
  955. begin
  956. scsi_wait;
  957. readln(infile,line);
  958. end;{proc}
  959. procedure read_input_integer(var ii:integer);
  960. var ss:str255;
  961.     work:longint;
  962. begin
  963. read_input(ss);
  964. stringtonum(ss,work);
  965. ii:=work;
  966. end;{proc}
  967.  
  968. procedure read_input_long(var jj:longint);
  969. var ss:str255;
  970. begin
  971. read_input(ss);
  972. stringtonum(ss,jj);
  973. end;{proc}
  974. procedure kill_nil;
  975. {for debugging}
  976. {put an odd value in memory location zero to hit nil's early}
  977. type lptr=^longint;
  978. var     p:lptr;
  979. begin
  980. p:=lptr(Pointer(0));
  981. P^:=$4E494C21;{NIL!}
  982. end;
  983. procedure myteshow;
  984. var i,nn,jj:integer;
  985.     ss,ww:str255;
  986. begin
  987.     nn:=statustext^^.nlines;
  988.     ss:='';
  989.     for i:=0 to nn do
  990.        begin
  991.        jj:=statustext^^.linestarts[i];
  992.       numtostring(jj,ww);
  993.       ss:=concat(concat(ss,ww),' ');       
  994.       end;
  995.       numtostring(statustext^^.telength,ww);
  996.       ss:=concat(concat(ss,ww),' ');       
  997.    poststatus(ss,byline);
  998.  
  999. end;{proc}
  1000.  
  1001. procedure dbashow;
  1002. var i:integer;
  1003.     ss,nn:str255;
  1004. begin
  1005.    myteshow;
  1006.    ss:='';
  1007.    for i:=1 to dbamax do
  1008.       begin
  1009.       numtostring(dbarray[i],nn);
  1010.       ss:=concat(concat(ss,nn),' ');
  1011.       end;
  1012.       numtostring(statustext^^.nlines,nn);
  1013.       ss:=concat(concat(ss,nn),' ');
  1014.      
  1015.    poststatus(ss,pathline);
  1016. end;{proc}
  1017.  
  1018. {$S core}
  1019. procedure write_end_flag(tag:str255);
  1020. var tab:char;
  1021. begin
  1022. if not outputopen then exit;
  1023. tab:=chr(9);
  1024. scsi_wait;
  1025. writeln(outfile,'*****',tab,tag);
  1026. end;{proc}
  1027.  
  1028. function test_end_flag(line:str255):boolean;
  1029. begin
  1030. test_end_flag:=copy(line,1,5)='*****';
  1031. end;{function}
  1032.  
  1033. function filenamecompare(aa,bb:myfilenametype):integer;
  1034. {compare filenames after trimming leading and trailing blanks}
  1035. {and mapping to uppercase}
  1036. {metaphore is sign of aa-bb
  1037.  if aa<bb return -1
  1038.  if aa=bb return 0
  1039.  if aa>bb return 1}
  1040.  label 10,20,30,40;
  1041.  var result:integer;
  1042.      w:str255;
  1043. begin
  1044. {while loops to trim blanks}
  1045. 10:if aa<>'' then
  1046.      if aa[1]=' ' then
  1047.         begin
  1048.         aa:=copy(aa,2,length(aa)-1);
  1049.         goto 10;
  1050.         end;
  1051. 20:if aa<>'' then
  1052.      if aa[length(aa)]=' ' then
  1053.         begin
  1054.         aa:=copy(aa,1,length(aa)-1);
  1055.         goto 20;
  1056.         end;
  1057. {while loops to trim blanks}
  1058. 30:if bb<>'' then
  1059.      if bb[1]=' ' then
  1060.         begin
  1061.         bb:=copy(bb,2,length(bb)-1);
  1062.         goto 30;
  1063.         end;
  1064. 40:if bb<>'' then
  1065.      if bb[length(bb)]=' ' then
  1066.         begin
  1067.         bb:=copy(bb,1,length(bb)-1);
  1068.         goto 40;
  1069.         end;
  1070. w:=aa;
  1071. uprstring(w,true);
  1072. aa:=w;
  1073. w:=bb;
  1074. uprstring(w,true);   
  1075. bb:=w;     
  1076. if aa=bb then
  1077.     result:=0
  1078. else if aa>bb then
  1079.     result:=1
  1080. else
  1081.     result:=-1;
  1082. filenamecompare:=result;
  1083. end;
  1084.  
  1085. {$S vols}
  1086. procedure find_vols;
  1087. label 88;
  1088. var 
  1089.   mypb:hparamblockrec;
  1090.   name:str255;
  1091.   err:oserr;
  1092.   index:integer;
  1093.   
  1094. begin
  1095. vcount:=0;
  1096. index:=0;
  1097. repeat
  1098.   index:=index+1;
  1099.   with mypb do
  1100.       begin
  1101.       iocompletion:=nil;
  1102.       name:='';
  1103.       ionameptr:=@name;
  1104.       iovrefnum:=0;
  1105.       iovolindex:=index;
  1106.       end;
  1107.   err:=pbhgetvinfo(@mypb,false);
  1108.  
  1109.   if err=noerr then
  1110.      begin
  1111.      if vcount>=maxvols then goto 88;
  1112.      vcount:=vcount+1;
  1113.      with newvols[vcount] do
  1114.      with mypb do
  1115.          begin
  1116.            volrefnum:=iovrefnum;
  1117.            vindex:=index;
  1118.            vname:=name;
  1119.            {compute size to the nearest K}
  1120.            vsize:=round((float(abs((longint(iovNmAlblks) and $0000FFFF)))*float(ioVAlBlkSiz))/1024);
  1121.            {shifted creation date}
  1122.            vcreation:=(iovcrdate shr creationdateshr) and creationdatemask;
  1123.            isboot:=(blessedbootvolwd=volrefnum);
  1124.            attributes:=iovAtrb;
  1125.            matchto:=0;
  1126.            checkvol:=true;
  1127.            if not checkfloppies then
  1128.               if vsize<floppycutoffsize then
  1129.                   begin
  1130.                      {skip floppies}
  1131.                      checkvol:=false;
  1132.                   end;
  1133.           if not checknonbootdrives then
  1134.               if not isboot then
  1135.                   begin
  1136.                     {skip non-boot drives}
  1137.                      checkvol:=false;
  1138.            end;
  1139.  
  1140.           end;
  1141.      end;
  1142.      
  1143. until(err<>noerr);
  1144. 88:
  1145. end;{find_vols}
  1146.  
  1147. procedure write_vols;
  1148. var i:integer;
  1149.     tab:string[1];
  1150. begin
  1151. tab:=chr(9);
  1152. if not outputopen then exit;
  1153. for i:=1 to vcount do
  1154.     with newvols[i] do
  1155.       begin
  1156.         scsi_wait;
  1157.         writeln(outfile,vname,tab,vsize,tab,vcreation,tab,ord(isboot));   
  1158.       end;
  1159. write_end_flag('end volumes');
  1160. end;{procedure write_vols}
  1161.  
  1162. procedure read_vols;
  1163. var line:str255;
  1164.     tokens:tokenstype;
  1165.     ntokens:integer;
  1166.     work:longint;
  1167. begin
  1168. oldvcount:=0;
  1169. if not inputopen then exit;
  1170. while not eof(infile) do
  1171.     begin
  1172.     read_input(line);
  1173.     if test_end_flag(line) then exit;
  1174.     tabscan(line,tokens,ntokens);
  1175.     if ntokens>=4 then
  1176.        begin
  1177.          oldvcount:=oldvcount+1;
  1178.          with oldvols[oldvcount] do
  1179.             begin
  1180.               vname:=tokens[1];
  1181.               stringtonum(tokens[2],vsize);
  1182.               stringtonum(tokens[3],vcreation);
  1183.               stringtonum(tokens[4],work);
  1184.               isboot:=boolean(ord(work));
  1185.               matchto:=0;
  1186.               {unused stuff}
  1187.                volrefnum:=0;
  1188.                vindex:=0;
  1189.                attributes:=0;
  1190.             end;
  1191.        end;
  1192.     end;
  1193.  
  1194. end;
  1195.  
  1196. procedure match_vols;
  1197. {decide what old volumes match what new volumes}
  1198. {this trys to match in several ways}
  1199. var iold,inew:integer;
  1200.     ccount:longint;
  1201.     nomatch:longint;
  1202.     w1,w2:str255;
  1203. begin
  1204. if oldvcount<=0 then exit;
  1205. if ((oldvcount=1) and (vcount=1)) then 
  1206.    begin
  1207.    {if one vol, always match}
  1208.    oldvols[1].matchto:=1;
  1209.    newvols[1].matchto:=1;
  1210.    exit;
  1211.    end;
  1212.  
  1213. {check for exact matches first}
  1214. for inew:=1 to vcount do
  1215.    if newvols[inew].matchto=0 then
  1216.    begin
  1217.    for iold:=1 to oldvcount do
  1218.       if oldvols[iold].matchto=0 then
  1219.       begin
  1220.        if (filenamecompare(oldvols[iold].vname,newvols[inew].vname)=0) then
  1221.         if oldvols[iold].vsize=newvols[inew].vsize then
  1222.           if oldvols[iold].vcreation=newvols[inew].vcreation then
  1223.              begin
  1224.              oldvols[iold].matchto:=inew;
  1225.              newvols[inew].matchto:=iold;
  1226.              end;{if match}   
  1227.       end;{for/if}
  1228.    end;{for/if}
  1229.  
  1230. {check for matches ignoring name}
  1231. {this assumes a rename may happen}
  1232. for inew:=1 to vcount do
  1233.    if newvols[inew].matchto=0 then
  1234.    begin
  1235.    for iold:=1 to oldvcount do
  1236.       if oldvols[iold].matchto=0 then
  1237.       begin
  1238.         if oldvols[iold].vsize=newvols[inew].vsize then
  1239.           if oldvols[iold].vcreation=newvols[inew].vcreation then
  1240.              begin
  1241.              oldvols[iold].matchto:=inew;
  1242.              newvols[inew].matchto:=iold;
  1243.              end;{if match}   
  1244.       end;{for/if}
  1245.    end;{for/if}
  1246.       
  1247. {match by name only}
  1248. {this will match a volume that has been reinitialized
  1249.  with the same name}
  1250. for inew:=1 to vcount do
  1251.    if newvols[inew].matchto=0 then
  1252.    begin
  1253.    for iold:=1 to oldvcount do
  1254.       if oldvols[iold].matchto=0 then
  1255.       begin
  1256.        if (filenamecompare(oldvols[iold].vname,newvols[inew].vname)=0) then
  1257.         if oldvols[iold].vsize=newvols[inew].vsize then
  1258.           if oldvols[iold].vcreation=newvols[inew].vcreation then
  1259.              begin
  1260.              oldvols[iold].matchto:=inew;
  1261.              newvols[inew].matchto:=iold;
  1262.              end;{if match}   
  1263.       end;{for/if}
  1264.    end;{for/if}   
  1265.    
  1266. nomatch:=0;
  1267. ccount:=0;
  1268. for inew:=1 to vcount do
  1269.    if newvols[inew].checkvol then
  1270.     begin
  1271.     ccount:=ccount+1;
  1272.        if newvols[inew].matchto=0 then 
  1273.                   nomatch:=nomatch+1;
  1274.     end;
  1275. if nomatch=0 then exit;
  1276. if not inputopen then exit;
  1277. numtostring(nomatch,w1);
  1278. numtostring(ccount,w2);
  1279. w1:=concat(concat(concat(concat(concat('Note: ',w1),' of '),w2),
  1280. ' mounted disk volumes to be checked do not match any in the input file. '),
  1281.  'No application changes will be reported on these volumes.');
  1282. wait_for_buttons(w1,continuebut);
  1283. end;{procedure}
  1284.  
  1285. {$S vols}  
  1286.         procedure sortnewvols(var X:myvolumearraytype;N:integer);
  1287.         {sort array of volumes in a consistent but arbitrary order}
  1288.         {this is done after matching with old volumes to put the
  1289.         new volumes in the same order, except for mismatches}
  1290.         
  1291. {        HEAP SORT
  1292. C
  1293. C       BASED ON PROGRAMMING PEARLS BY JON BENTLEY P 182
  1294. C       X IS THE ARRAY CONTAINING NX ELEMENTS TO BE SORTED
  1295. C       CALL SWAPX(I,J) SWAPS X(I) WITH X(J)
  1296. C       GEX(X,I,J) IS TRUE IFF X(I) >= X(J)
  1297. C       GTX(X,I,J) IS TRUE IFF X(I) > X(J)
  1298. C}
  1299.  
  1300.  
  1301.        var i: integer;
  1302.        
  1303.        
  1304. procedure SWAPX(I:integer;J:integer);
  1305. var     T:myvolumerec;
  1306. {swap new vols and update matchto fields in new vols}      
  1307. begin
  1308.         T:=X[I];
  1309.         X[I]:=X[J];
  1310.         X[J]:=T;
  1311.         
  1312.         IF X[I].matchto<>0 then
  1313.             oldvols[X[I].matchto].matchto:=I;
  1314.         IF X[J].matchto<>0 then
  1315.             oldvols[X[J].matchto].matchto:=J;
  1316.             
  1317. END; {of procedure swapx}
  1318.  
  1319. FUNCTION GTX(I:integer;J:integer):boolean;
  1320. var filecomp:integer;
  1321. begin
  1322.        {sort by order of matches then index }
  1323.        gtx:=false;
  1324.        
  1325.        if (X[I].matchto>X[J].matchto) then
  1326.           begin
  1327.            gtx:=true
  1328.           end
  1329.        else if (X[I].matchto=X[J].matchto) then
  1330.           begin
  1331.             if (X[I].vindex>X[J].vindex)then
  1332.                 begin
  1333.                     gtx:=true; 
  1334.                 end;
  1335.            end;
  1336.                    
  1337. end;
  1338.         
  1339. FUNCTION GEX(I:integer;J:integer):boolean;
  1340. var filecomp:integer;
  1341. begin
  1342.        {sort by order of matches then index }
  1343.        gex:=false;
  1344.        
  1345.        if (X[I].matchto>X[J].matchto) then
  1346.           begin
  1347.            gex:=true
  1348.           end
  1349.        else if (X[I].matchto=X[J].matchto) then
  1350.           begin
  1351.             if (X[I].vindex>=X[J].vindex)then
  1352.                 begin
  1353.                     gex:=true; 
  1354.                 end;
  1355.            end;
  1356.  
  1357. END;
  1358.        
  1359. procedure siftdown(L:integer;U:integer);
  1360.     label 300,999{return};
  1361.     var
  1362.         i,child:integer;
  1363.         
  1364. begin
  1365.         
  1366. {
  1367. C
  1368. C       BEFORE MAXHEAP(L+1,U)
  1369. C       AFTER  MAXHEAP(L,U)
  1370. }
  1371.         I:=L;
  1372.         
  1373.         {LOOP}
  1374. 300:
  1375. {
  1376. C               INVARIANT: MAXHEAP(L,U) EXCEPT PERHAPS
  1377. C                       BETWEEN I AND ITS (0,1 OR 2) CHILDREN
  1378. C
  1379. }
  1380.                 CHILD:=2*I;
  1381.  
  1382.                 IF CHILD > U  then goto 999;
  1383. {
  1384. C
  1385. C               IF C+1 <= U AND X(C+1) > X(C) THEN C=C+1
  1386. C
  1387. }
  1388.                 IF(CHILD+1 <= U) THEN
  1389.                 IF(GTX(CHILD+1,CHILD))THEN
  1390.                         CHILD:=CHILD+1;
  1391.  
  1392. {                
  1393. C
  1394. C               CHILD IS THE GREATEST CHILD OF I
  1395. C
  1396. C               IF X(I) >= X(CHILD) THEN RETURN
  1397. C
  1398. }
  1399.                 IF(GEX(I,CHILD)) then goto 999;
  1400.                 
  1401. {                
  1402. C
  1403. C               X(I) IS LESS THAN ITS GREATEST CHILD SO SWAP IT DOWNWARD
  1404. C               AND REPEAT LOOP
  1405. C
  1406. }
  1407.                 SWAPX(CHILD,I);
  1408.                 I:=CHILD;
  1409.                 GOTO 300;
  1410.         {END LOOP}
  1411. 999:{return}
  1412. END; {of proc siftdown}
  1413.  
  1414.  
  1415.        
  1416. begin {main body of sortnewvols}
  1417.  
  1418.         for I:=N div 2 downto 1 do
  1419.         begin
  1420.        { echo(i);}
  1421.         SIFTDOWN(I,N);
  1422.         end;
  1423.  
  1424.         {echo(0);}
  1425.  
  1426.         for I:=N downto 2 do
  1427.         begin
  1428.           {  echo(i);}
  1429.                 SWAPX(1,I);
  1430.                 {echo(i);}
  1431.                 SIFTDOWN(1,I-1);
  1432.                { echo(i);}
  1433.          end;
  1434.  
  1435.  
  1436.  
  1437. END; {sortnewvols}
  1438.  
  1439. procedure dovols;
  1440. {multi-volume processing}
  1441. begin
  1442. find_vols;
  1443. read_vols;
  1444. match_vols;
  1445. sortnewvols(newvols,vcount);
  1446. end;
  1447.  
  1448. {$S appl}
  1449. procedure note_application( fname:str255;
  1450.                             pdirID:longint;
  1451.                             index:integer;
  1452.                             mycpb:CInfoPBRec;
  1453.                             hidden:boolean);
  1454.                            
  1455.  (*
  1456.        {info on applications}
  1457.       applinforec=record
  1458.                   thesize:longint;
  1459.                   creator:OStype;
  1460.                   creationdate:longint;
  1461.                   dirid:longint;
  1462.                   filename:myfilenametype;
  1463.                   unsafecount:integer;
  1464.                   checksum:integer;
  1465.                   fileindex:integer;
  1466.                   flags:integer;
  1467.                   end;
  1468.                   
  1469.       applinfoarray=array[1..maxappl] of applinforec;
  1470.       applinfoarrayptr=^applinfoarray;
  1471.  
  1472.  *)
  1473. {add to a list of applications in memory}
  1474. begin
  1475. if acount<maxappl then
  1476.   begin
  1477.      poststatus(fname,fileline);
  1478.      acount:=acount+1;
  1479.      with ainfo^[acount] do
  1480.         begin
  1481.         flags:=currentvolumesubscript and applvolumemask;{save volume}
  1482.         checksum:=0;
  1483.         checksize:=0;
  1484.         if fastapplcheck then 
  1485.            unsafecount:=notcounted
  1486.         else 
  1487.            unsafecount:=0;
  1488.         filename:=fname;
  1489.         dirid:=pdirid;
  1490.         if hidden then
  1491.            begin
  1492.              {make as hidden non-application file}
  1493.               flags:=flags or applInvisiblemask;
  1494.            end;
  1495.         with mycpb do
  1496.           begin
  1497.              thesize:=ioflRLgLen;{logical size of resource fork}
  1498.              creationdate:=(ioflcrdat shr creationdateshr) and creationdatemask;
  1499.              {creation date and time}
  1500.              creator:=ioFlFndrInfo.fdcreator;
  1501.           end;{with}
  1502.         end;{with}
  1503.   end
  1504. else
  1505.   begin
  1506.    poststatus('Max applications exceeded.',errorline);
  1507.   end;
  1508. end;{procedure}
  1509.  
  1510. PROCEDURE EnumerAPPLShell;
  1511. {search applications on current default volume staring with root}
  1512. VAR
  1513.  
  1514.       myCPB: CInfoPBRec;
  1515.       err: OSErr;  
  1516.       myWDPB: WDPBRec;
  1517.    DirIDToSearch:Longint;
  1518.    fname:str255;
  1519.    p:wordptr;
  1520.    accessrights:integer;
  1521.  
  1522. PROCEDURE EnumerateAPPLCatalog(dirIDToSearch: longint);
  1523.  
  1524. VAR
  1525.   index:    integer;  
  1526.  
  1527.   
  1528.  
  1529. Begin {EnumerateAPPLCatalog}
  1530.  
  1531.     index:= 1;
  1532.  
  1533.     repeat
  1534.  
  1535.         FName:= '';  {nil out name}
  1536.         myCPB.ioFDirIndex:= index;
  1537.         myCPB.ioDrDirID:= dirIDToSearch; {we need to do this every time through}
  1538.   p:=@mycpb.ioflAttrib;{clear word with appleshare permissions in 2nd byte}
  1539.   p^:=0;
  1540.  
  1541.         err:= PBGetCatInfo(@myCPB,FALSE);
  1542.  
  1543.  
  1544.         If err = noErr then 
  1545.  
  1546.             if BitTst(@myCPB.ioFlAttrib,3) then 
  1547.       Begin 
  1548.        {we have a dir}
  1549.           p:=@mycpb.ioflAttrib;{appleshare permissions are at offset 31}
  1550.           accessrights:=p^ and $00FF;
  1551.           if (accessrights and appleshareaccessmask)=0 then
  1552.              begin
  1553.              {only descend tree if we have specified rights}
  1554.                       EnumerateAPPLCatalog(myCPB.ioDrDirID);
  1555.              end;
  1556.                  err:= 0;  {clear error return on way back}
  1557.             End {if dir}
  1558.          Else 
  1559.       Begin
  1560.         {we have a file}
  1561.         {test if application or invisible file}
  1562.         if (myCPB.ioFlFndrInfo.fdtype='APPL')then
  1563.              begin
  1564.                {It is an application, add it to list in memory}
  1565.                note_application(fname,dirIDToSearch,index,mycpb,false)
  1566.                end
  1567.         else if (myCPB.ioFlFndrInfo.fdflags and fInvisible)<>0 then
  1568.              begin
  1569.                {It is a hidden file add it to list in memory}
  1570.                note_application(fname,dirIDToSearch,index,mycpb,true)
  1571.              end;
  1572.             End; {end if} 
  1573.  
  1574.         index:= index + 1;
  1575.  
  1576. until err <> noErr;
  1577.  
  1578. End;  {EnumerateAPPLCatalog}
  1579.  
  1580.  
  1581.  
  1582. Begin {EnumerAPPLShell}
  1583.  
  1584. DirIDToSearch:=2;{root}
  1585.  
  1586.     err:= PBHGetVol(@myWDPB,FALSE);        {get the default volume}
  1587.  
  1588.     with MyCPB do Begin
  1589.         iocompletion:= Nil;
  1590.         ioNamePtr:= @FName;
  1591.         ioVRefNum:= myWDPB.ioVRefNum;      {for now, default vol, set this to what you want}
  1592.     End;  {with}
  1593.  
  1594.     EnumerateAPPLCatalog(DIRIDTOSEARCH);{DirID 2, is the root level}
  1595.  
  1596. End;  {procedure EnumerAPPLShell}
  1597.  
  1598.         procedure sortapplications(var X:applinfoarrayptr;N:integer);
  1599.         {sort array of applications and their checksums}
  1600. {        HEAP SORT
  1601. C
  1602. C       BASED ON PROGRAMMING PEARLS BY JON BENTLEY P 182
  1603. C       X IS THE ARRAY CONTAINING NX ELEMENTS TO BE SORTED
  1604. C       CALL SWAPX(I,J) SWAPS X(I) WITH X(J)
  1605. C       GEX(X,I,J) IS TRUE IFF X(I) >= X(J)
  1606. C       GTX(X,I,J) IS TRUE IFF X(I) > X(J)
  1607. C}
  1608.  
  1609.  
  1610.        var i: integer;
  1611.        
  1612.        
  1613. procedure SWAPX(I:integer;J:integer);
  1614. var     T:applinforec;
  1615.         
  1616. begin
  1617.         T:=X^[I];
  1618.         X^[I]:=X^[J];
  1619.         X^[J]:=T;
  1620. END; {of procedure swapx}
  1621.  
  1622. FUNCTION GTX(I:integer;J:integer):boolean;
  1623. var filecomp:integer;
  1624. begin
  1625.    {sort by creator signature,creation date,filename,volume,dirID}
  1626.    gtx:=false;
  1627.    if (X^[I].creator>X^[J].creator) then
  1628.       begin{1}
  1629.         gtx:=true;
  1630.       end{1}
  1631.    else if (X^[I].creator=X^[J].creator) then
  1632.       begin{2}
  1633.         if (X^[I].creationdate>X^[J].creationdate) then
  1634.            begin{3}
  1635.              gtx:=true;
  1636.            end{3}
  1637.         else if (X^[I].creationdate=X^[J].creationdate) then
  1638.            begin{4}
  1639.              filecomp:=filenamecompare(X^[I].filename,X^[J].filename);
  1640.              if filecomp>0{(X^[I].filename>X^[J].filename)} then
  1641.                 begin{5}
  1642.                   gtx:=true;
  1643.                 end{5}
  1644.              else if filecomp=0{(X^[I].filename=X^[J].filename)} then
  1645.                  begin{6}
  1646.                   if (X^[I].flags and applvolumemask)>(X^[J].flags and applvolumemask) then
  1647.                       begin{7}
  1648.                          gtx:=true;
  1649.                       end{7}
  1650.                   else if (X^[I].flags and applvolumemask)=(X^[J].flags and applvolumemask) then
  1651.                      begin{8}
  1652.                        if (X^[I].dirID>X^[J].dirID) then
  1653.                        begin{9}
  1654.                           gtx:=true;
  1655.                        end;{9}
  1656.                      end;{8}
  1657.                   end;{6}
  1658.               end;{4}
  1659.          end;{2}
  1660.                
  1661. end;
  1662.         
  1663. FUNCTION GEX(I:integer;J:integer):boolean;
  1664. var filecomp:integer;
  1665. begin
  1666.    {sort by creator signature,creation date,filename,volume,dirID}
  1667.    gex:=false;
  1668.    if (X^[I].creator>X^[J].creator) then
  1669.       begin{1}
  1670.         gex:=true;
  1671.       end{1}
  1672.    else if (X^[I].creator=X^[J].creator) then
  1673.       begin{2}
  1674.         if (X^[I].creationdate>X^[J].creationdate) then
  1675.            begin{3}
  1676.              gex:=true;
  1677.            end{3}
  1678.         else if (X^[I].creationdate=X^[J].creationdate) then
  1679.            begin{4}
  1680.              filecomp:=filenamecompare(X^[I].filename,X^[J].filename);
  1681.              if filecomp>0{(X^[I].filename>X^[J].filename)} then
  1682.                 begin{5}
  1683.                   gex:=true;
  1684.                 end{5}
  1685.              else if filecomp=0{(X^[I].filename=X^[J].filename)} then
  1686.                  begin{6}
  1687.                   if (X^[I].flags and applvolumemask)>(X^[J].flags and applvolumemask) then
  1688.                       begin{7}
  1689.                          gex:=true;
  1690.                       end{7}
  1691.                   else if (X^[I].flags and applvolumemask)=(X^[J].flags and applvolumemask) then
  1692.                      begin{8}
  1693.                        if (X^[I].dirID>=X^[J].dirID) then
  1694.                        begin{9}
  1695.                           gex:=true;
  1696.                        end;{9}
  1697.                      end;{8}
  1698.                   end;{6}
  1699.               end;{4}
  1700.          end;{2}
  1701.  
  1702. END;
  1703.        
  1704. procedure siftdown(L:integer;U:integer);
  1705.     label 300,999{return};
  1706.     var
  1707.         i,child:integer;
  1708.         
  1709. begin
  1710.         
  1711. {
  1712. C
  1713. C       BEFORE MAXHEAP(L+1,U)
  1714. C       AFTER  MAXHEAP(L,U)
  1715. }
  1716.         I:=L;
  1717.         
  1718.         {LOOP}
  1719. 300:
  1720. {
  1721. C               INVARIANT: MAXHEAP(L,U) EXCEPT PERHAPS
  1722. C                       BETWEEN I AND ITS (0,1 OR 2) CHILDREN
  1723. C
  1724. }
  1725.                 CHILD:=2*I;
  1726.  
  1727.                 IF CHILD > U  then goto 999;
  1728. {
  1729. C
  1730. C               IF C+1 <= U AND X^(C+1) > X^(C) THEN C=C+1
  1731. C
  1732. }
  1733.                 IF(CHILD+1 <= U) THEN
  1734.                 IF(GTX(CHILD+1,CHILD))THEN
  1735.                         CHILD:=CHILD+1;
  1736.  
  1737. {                
  1738. C
  1739. C               CHILD IS THE GREATEST CHILD OF I
  1740. C
  1741. C               IF X^(I) >= X^(CHILD) THEN RETURN
  1742. C
  1743. }
  1744.                 IF(GEX(I,CHILD)) then goto 999;
  1745.                 
  1746. {                
  1747. C
  1748. C               X^(I) IS LESS THAN ITS GREATEST CHILD SO SWAP IT DOWNWARD
  1749. C               AND REPEAT LOOP
  1750. C
  1751. }
  1752.                 SWAPX(CHILD,I);
  1753.                 I:=CHILD;
  1754.                 GOTO 300;
  1755.         {END LOOP}
  1756. 999:{return}
  1757. END; {of proc siftdown}
  1758.  
  1759.  
  1760.        
  1761. begin {main body of sortapplications}
  1762.  
  1763.         for I:=N div 2 downto 1 do
  1764.         begin
  1765.        { echo(i);}
  1766.         SIFTDOWN(I,N);
  1767.         end;
  1768.  
  1769.         {echo(0);}
  1770.  
  1771.         for I:=N downto 2 do
  1772.         begin
  1773.           {  echo(i);}
  1774.                 SWAPX(1,I);
  1775.                 {echo(i);}
  1776.                 SIFTDOWN(1,I-1);
  1777.                { echo(i);}
  1778.          end;
  1779.  
  1780.  
  1781.  
  1782. END; {sortapplications}
  1783.  
  1784. procedure APPLsummary;
  1785. {write summary of info on applications}
  1786. var i:integer;
  1787.     tab:char;
  1788.     tags:str255;
  1789.     dname:str255;
  1790. begin
  1791. set_default_blessed;
  1792. tab:=chr(9);
  1793. if not outputopen then exit;
  1794. poststatus('Writing Application Summary Output',pathline);
  1795. for i:=1 to acount do
  1796.   with ainfo^[i] do
  1797.      begin
  1798.          dname:='?';
  1799.          folder_name(dirid,dname);
  1800.          tags:='';
  1801.          if inputopen then
  1802.          begin
  1803.            if (flags and applexactmatchmask)<>applexactmatchmask then
  1804.              begin
  1805.              if (flags and applrenamemask)=applrenamemask then
  1806.                 begin
  1807.                    {moved or renamed or duplicated}
  1808.                    tags:='moved/renamed??';
  1809.                 end
  1810.              else
  1811.                 begin
  1812.                   if (flags and applchangedmask)=applchangedmask then
  1813.                     begin
  1814.                       {changed}
  1815.                       tags:='changed??';
  1816.                     end
  1817.                   else
  1818.                     begin
  1819.                        {"new"}
  1820.                        tags:='new??';
  1821.                     end
  1822.                 end;
  1823.             end;
  1824.             if (flags and appldangermask)=appldangermask then
  1825.                 begin
  1826.                    tags:=concat(tags,' Danger??');
  1827.                 end;
  1828.           end;{inputopen}
  1829.           if (flags and applinvisiblemask)<>0 then
  1830.                  begin
  1831.                    tags:=concat(tags,'(hidden)');
  1832.                  end;
  1833.       scsi_wait;
  1834.       write(outfile,creator:4,tab,
  1835.                       creationdate,tab,
  1836.                       filename,tab,
  1837.                       dirid,tab,
  1838.                       (flags and applvolumemask),tab,
  1839.                       thesize,tab,
  1840.                       unsafecount,tab,
  1841.                       checksize,tab,
  1842.                       checksum,tab,
  1843.                       dname);
  1844.       scsi_wait;
  1845.      if tags='' then writeln(outfile) else writeln(outfile,tab,tags);
  1846.      
  1847.      end;{for/with}
  1848.  
  1849. write_end_flag('end applications and hidden files');
  1850. end;{applsummary}
  1851.  
  1852. procedure copyapplsummary;
  1853. {copy old applications summary when no application checks are done}
  1854. label 88;
  1855. var line:str255;
  1856.  
  1857. begin
  1858. if not outputopen then exit;
  1859.  
  1860. if inputopen then
  1861.    begin
  1862.       while(not(eof(infile))) do
  1863.          begin
  1864.           read_input(line);
  1865.           if test_end_flag(line) then goto 88;
  1866.           scsi_wait;
  1867.           writeln(outfile,line);
  1868.          end;
  1869.       88:
  1870.    end;
  1871. write_end_flag('end applications and hidden files(copy)');
  1872. end;
  1873.  
  1874. procedure scan_all_vols;
  1875. label 88;
  1876. var i:integer;
  1877.     err:oserr;
  1878. begin
  1879. for i:=1 to vcount do
  1880.    begin
  1881.    if newvols[i].checkvol then 
  1882.       begin
  1883.       err:=setvol(nil,newvols[i].volrefnum);
  1884.       if err=noerr then
  1885.            begin 
  1886.            poststatus(concat('Scan applications and hidden files:',newvols[i].vname),pathline);
  1887.            currentvolumesubscript:=i;
  1888.            enumerAPPLshell;
  1889.            end;
  1890.       end;
  1891.    end;
  1892.  
  1893. set_default_blessed;
  1894. end;
  1895.  
  1896. procedure docheck_applications;
  1897.  
  1898. begin
  1899. set_default_blessed;
  1900. acount:=0;
  1901. clear_to_end(pathline);
  1902. scan_all_vols;
  1903.  
  1904. clear_to_end(fileline);
  1905. poststatus('Checksum applications',pathline);
  1906. checksum_all_appl;
  1907. clear_to_end(pathline);
  1908. Poststatus('Sort Application info',pathline);
  1909. sortapplications(ainfo,acount);
  1910. if fastapplcheck then
  1911.         poststatus('Compare application sizes',pathline)
  1912. else
  1913.         poststatus('Compare application resource checks',pathline);
  1914. clear_to_end(fileline);
  1915. detail_appl_check;
  1916. set_default_blessed;
  1917. clear_to_end(pathline);
  1918. show_appl_detail_changes;
  1919. end;{procedure}
  1920.  
  1921.  
  1922. {$S myres}
  1923. procedure initmypath(var mypath:myresPathtype);
  1924. const bigneg=-16000;
  1925.       startsize=32;
  1926. begin
  1927. with mypath do
  1928.    begin
  1929.    volref:=0;
  1930.    fileref:=0;
  1931.    filename:='';
  1932.    {absolute offsets}
  1933.    offset_to_res_data:=bigneg;
  1934.    offset_to_res_map:=bigneg;
  1935.    offset_to_typelist:=bigneg;{derived}
  1936.    offset_to_namelist:=bigneg;{derived}
  1937.    typelist:=myresTypeListHandle(newhandle(startsize));
  1938.    reflist:=myresReflistHandle(newhandle(startsize));
  1939.    resdata:=newhandle(startsize);
  1940.    current_type:='    ';
  1941.    current_type_subscript:=0;
  1942.    status:=pathbad;{state of path}
  1943.    ntypes:=0;{number of types}
  1944.    nrefs:=0;{number of references to current type}
  1945.    end;
  1946.    
  1947. end;{proc}
  1948. function my_openRF_readonly(filename:str255;
  1949.          vrefnum:integer; var refnum:integer):oserr;
  1950.          
  1951. {Open Resource File - as a file - read only}
  1952.  
  1953. var  mypb:Paramblockrec;
  1954.      err:oserr;
  1955. begin
  1956. with mypb do
  1957.   begin
  1958.     iocompletion:=nil;
  1959.     ionameptr:=@filename;
  1960.     ioVrefnum:=vrefnum;
  1961.     iorefnum:=0;{dummy for bad returns}
  1962.     ioVersNum:=0;
  1963.     IoPermssn:=fsRdPerm;{read only}
  1964.     ioMisc:=nil; 
  1965.   end;{with}
  1966. scsi_wait;
  1967. err:=PBOpenRF(@mypb,false);  
  1968. refnum:=mypb.iorefnum;
  1969. my_openRF_readonly:=err;
  1970. end;{proc}
  1971. function setmytype( var mypath:myresPathtype;
  1972.                     index:integer;
  1973.                     var atype:restype
  1974.                     ):boolean;  
  1975. {set the current type, return true if suceeded}
  1976. {eith index (1 to ntypes) or set by type}
  1977. label 99,88,77;
  1978. var offset:longint;
  1979.     ii:integer;
  1980.     rlsize:longint;
  1981.     bcount:longint;
  1982. begin
  1983. with mypath do
  1984.   begin
  1985.   if status<typelistopen then goto 99;
  1986.   if index>0 then
  1987.      begin
  1988.      {pick type by index}
  1989.      if index>ntypes then goto 99;
  1990.       {**R-}
  1991.       current_type:=typelist^^[index-1].thetype;
  1992.       offset:=typelist^^[index-1].offset_typelist_to_reflist;
  1993.       nrefs:=typelist^^[index-1].count_minus_one+1;
  1994.       {**R+}
  1995.       atype:=current_type;
  1996.       current_type_subscript:=index-1;
  1997.      end
  1998.    else
  1999.      begin
  2000.      {pick type by name}
  2001.      for ii:=0 to nrefs-1 do
  2002.         begin
  2003.         {**R-}
  2004.           if typelist^^[ii].thetype=atype then 
  2005.               begin
  2006.                  current_type:=typelist^^[ii].thetype;
  2007.                  offset:=typelist^^[ii].offset_typelist_to_reflist;
  2008.                  nrefs:=typelist^^[ii].count_minus_one+1;
  2009.                  current_type_subscript:=ii;
  2010.                  goto 77;{match}
  2011.               end;
  2012.          {**R+}
  2013.         end;
  2014.       goto 99;{fail}
  2015.      end;
  2016.   77:
  2017.   {load the reference list for this type}
  2018.   Hunlock(handle(reflist));    
  2019.   status:=typelistopen;
  2020.   offset:=offset_to_typelist+offset;{compute absolute offset}
  2021.   
  2022.   {reserve memory}
  2023.   rlsize:=nrefs*12;
  2024.   hunlock(handle(reflist));
  2025.   sethandlesize(handle(reflist),rlsize);
  2026.   if memerror<>noerr then goto 99;
  2027.   
  2028.   Hlock(handle(reflist));
  2029.   {actually do the read}
  2030.    if SetFPos(fileref,fsFromStart,offset)<>noerr then goto 88;
  2031.    bcount:=rlsize;
  2032.    scsi_wait;
  2033.    if FsRead(fileref,bcount,ptr(reflist^))<>noerr then goto 88;
  2034.    if bcount<>rlsize then goto 88;
  2035.    {we've got the reflist "open"}
  2036.    hunlock(handle(reflist));
  2037.    status:=reflistopen;
  2038.   end;{while}  
  2039. setmytype:=true;
  2040. exit;{normal}
  2041.   88:
  2042.   Hunlock(handle(mypath.reflist));
  2043.   mypath.status:=typelistopen;
  2044.   99:
  2045.   setmytype:=false;{failure}
  2046. end;{setmytype}
  2047.  
  2048. Function CopyResData(  var mypath:myresPathtype;
  2049.                             var index:integer;
  2050.                             var id:integer;
  2051.                             var  psize:longint;
  2052.                             var  pattr:integer;
  2053.                             var  pname:str255 ):boolean;
  2054. {copy resource data to handle
  2055.  either index(1 to nrefs) or search for id}
  2056.  const offsetmask=$00FFFFFF;
  2057.        ash=24;
  2058.        amask=$000000FF;
  2059. label 99,88,77,85;
  2060. var offset,noffset:longint;
  2061.     ii:integer;
  2062.     bcount:longint;
  2063.     rdsize:longint;
  2064.     noname:boolean;
  2065.     nsize:integer;
  2066. begin
  2067. psize:=0;
  2068. pname:='';
  2069. pattr:=0;
  2070. with mypath do
  2071.   begin
  2072.   if status<reflistopen then goto 99;
  2073.   hlock(handle(reflist));
  2074.   if index>0 then
  2075.      begin
  2076.      {**R-}
  2077.       offset:=(reflist^^[index-1].attrib_and_offset) and offsetmask;
  2078.       pattr:=((reflist^^[index-1].attrib_and_offset) shr ash) and amask;
  2079.       id:=reflist^^[index-1].theid;
  2080.       noffset:=reflist^^[index-1].offset_namelist_to_name;
  2081.      {**R+}
  2082.      end
  2083.   else
  2084.      begin
  2085.      for ii:=0 to ntypes-1 do
  2086.        begin
  2087.        {**R-}
  2088.         if reflist^^[ii].theid=id then
  2089.            begin
  2090.              offset:=(reflist^^[ii].attrib_and_offset) and offsetmask;
  2091.              pattr:=((reflist^^[ii].attrib_and_offset) shr ash) and amask;
  2092.              noffset:=reflist^^[ii].offset_namelist_to_name;
  2093.              goto 77;
  2094.            end;
  2095.         {**R+}
  2096.        end;{for}
  2097.       hunlock(handle(reflist));
  2098.        goto 88;{fail}
  2099.      77:  
  2100.      end;
  2101.   hunlock(handle(reflist));
  2102.      
  2103.   {convert to absolute offsets}
  2104.   offset:=offset_to_res_data+offset;
  2105.   noname:=(noffset=-1);
  2106.   if not noname then noffset:=noffset+offset_to_namelist;
  2107.   
  2108.    if not noname then
  2109.          begin
  2110.          {get name length}
  2111.          if SetFPos(fileref,fsFromStart,noffset)<>noerr then goto 88;
  2112.          bcount:=2;
  2113.          scsi_wait;
  2114.          if FsRead(fileref,bcount,@nsize)<>noerr then goto 88;
  2115.          if bcount<>2 then goto 88;
  2116.          nsize:=(nsize shr 8) and $00FF;{convert first byte to integer}
  2117.          nsize:=nsize+1;{length of pascal string}
  2118.          {get the whole name}
  2119.          {get name length}{use relative positioning}
  2120.          scsi_wait;
  2121.          if SetFPos(fileref,fsfrommark,-2)<>noerr then goto 88;
  2122.          bcount:=nsize;
  2123.          scsi_wait;
  2124.          if FsRead(fileref,bcount,@pname)<>noerr then goto 88;
  2125.          if bcount<>nsize then goto 88;
  2126.          end;{if name}
  2127.  
  2128.   {get data length} 
  2129.   scsi_wait;
  2130.    if SetFPos(fileref,fsfromstart,offset)<>noerr then goto 88;
  2131.    bcount:=4;
  2132.    scsi_wait;
  2133.    if FsRead(fileref,bcount,@rdsize)<>noerr then goto 88;
  2134.    if bcount<>4 then goto 88;
  2135.     
  2136.    {allocate space for data}
  2137.    hunlock(resdata);
  2138.    sethandlesize(resdata,rdsize);
  2139.    if memerror<>noerr then goto 99;
  2140.       
  2141.    {read in the data}
  2142.    {offset:=offset+4;}{read from mark after data length}
  2143.    {if SetFPos(fileref,fsfromStart,offset)<>noerr then goto 85;}
  2144.    bcount:=rdsize;
  2145.    hlock(resdata);
  2146.    scsi_wait;
  2147.    if FsRead(fileref,bcount,(resdata^))<>noerr then goto 85;
  2148.    hunlock(resdata);
  2149.    if bcount<>rdsize then goto 85;
  2150.    {we have got it}
  2151.    psize:=rdsize+4;{add 4 to size to make consistent with 
  2152.                     result of SIzeResource}
  2153.       
  2154.   end;{with}
  2155.   
  2156. Hunlock(mypath.resdata);  
  2157. copyResData:=true;
  2158. exit;
  2159. {failure}
  2160.   85:
  2161.   88:
  2162.   99:
  2163. Hunlock(mypath.resdata);
  2164. copyResData:=false;
  2165. end;{function}
  2166.               
  2167. procedure closepath(var mypath:myresPathtype);
  2168. var err:oserr;
  2169. begin
  2170.  
  2171. if goodhandle(handle(mypath.reflist),'closepath1') then
  2172.      Hunlock(handle(mypath.reflist))
  2173. else
  2174.      sysbeep(1);
  2175. if goodhandle(handle(mypath.typelist),'closepath2') then
  2176.      Hunlock(handle(mypath.typelist))
  2177. else
  2178.      sysbeep(1);
  2179. if goodhandle(mypath.resdata,'closepath3')then
  2180.      Hunlock(handle(mypath.resdata))
  2181. else
  2182.      sysbeep(1);
  2183.  
  2184. if mypath.reflist<>nil then
  2185.     sethandlesize(handle(mypath.reflist ),32);
  2186. if mypath.typelist<>nil then
  2187.     sethandlesize(handle(mypath.typelist ),32);
  2188. if mypath.resdata<>nil then
  2189.     sethandlesize(handle(mypath.resdata ),32);
  2190. if mypath.status>=pathempty then
  2191.          begin 
  2192.         { dbarray[dbaopen]:=dbarray[dbaopen]-1;}
  2193.          err:=FSclose(mypath.fileref);
  2194.          end;
  2195.          
  2196. mypath.status:=pathbad;
  2197. end;{proc closepath}
  2198.  
  2199. function openpath(var mypath:myresPathtype;pfilename:str255;pvolref:integer):oserr;
  2200. label 99,98;
  2201. const myerr=-999;
  2202.       minimumRFsize=16;
  2203. var err,err2:oserr;
  2204.     lsize:longint;
  2205.     bcount:longint;
  2206.     theeof:longint;
  2207. begin
  2208. err:=myerr;
  2209. with mypath do
  2210.    begin
  2211.       volref:=pvolref;
  2212.       filename:=pfilename;
  2213.       current_type:='    ';
  2214.       ntypes:=0;
  2215.       nrefs:=0;
  2216.       status:=pathbad;
  2217.        
  2218.       {open resource fork}
  2219.       err:=my_openRF_readonly(pfilename,pvolref,fileref); 
  2220.       if err<>noerr then goto 99;
  2221.       {dbarray[dbaopen]:=dbarray[dbaopen]+1;}
  2222.       status:=pathopen;
  2223.       
  2224.       {get Eof to see if this file has a resource fork}
  2225.       err:=geteof(fileref,theeof);
  2226.       if err<>noerr then goto 99;
  2227.       {do a normal exit on an empty resource fork}
  2228.       if theeof<minimumRFsize then
  2229.             begin
  2230.               ntypes:=0;
  2231.               nrefs:=0;
  2232.               status:=pathempty;
  2233.               openpath:=noerr;
  2234.               closepath(mypath);
  2235.               exit;
  2236.            end;
  2237.               
  2238.       
  2239.       
  2240.       {get offsets to data and start}
  2241.       err:=SetFPos(fileref,fsFromStart,0);
  2242.       if err<>noerr then goto 99;
  2243.       bcount:=8;
  2244.       scsi_wait;
  2245.       err:=FsRead(fileref,bcount,@offset_to_res_data);
  2246.       if err<>noerr then goto 99;
  2247.       if bcount<>8 then goto 98;
  2248.  
  2249.       {get resource map}
  2250.       err:=SetFPos(fileref,fsFromStart,offset_to_res_map);
  2251.       if err<>noerr then goto 99;
  2252.       bcount:=sizeof(myresMaptype);
  2253.       scsi_wait;
  2254.       err:=FsRead(fileref,bcount,@map);
  2255.       if err<>noerr then goto 99;
  2256.       if bcount<>sizeof(myresMaptype) then goto 98;
  2257.       
  2258.       {compute absolute offset to type list,name list}
  2259.       offset_to_typelist:=offset_to_res_map+map.offset_map_to_typelist;
  2260.       offset_to_namelist:=offset_to_res_map+map.offset_map_to_namelist;
  2261.    
  2262.       {get number of types}
  2263.       err:=SetFPos(fileref,fsFromStart,offset_to_typelist);
  2264.       if err<>noerr then goto 99;
  2265.       bcount:=2 {sizeof(myresMaptype)};
  2266.       scsi_wait;
  2267.       err:=FsRead(fileref,bcount,@ntypes);
  2268.       if err<>noerr then goto 99;
  2269.       if bcount<>2 {sizeof(myresMaptype)}then goto 98;
  2270.       ntypes:=ntypes+1;
  2271.  
  2272.       {resize handle for type list}
  2273.       lsize:=8*ntypes;
  2274.       Hunlock(handle(typelist));
  2275.       sethandlesize(handle(typelist),lsize);
  2276.       if memerror<>noerr then 
  2277.           begin
  2278.           err:=memerror;
  2279.           goto 99;
  2280.           end;
  2281.           
  2282.       status:=typelistopen;
  2283.       hlock(handle(typelist));
  2284.       
  2285.       {read type list into handle}
  2286.      { err:=SetFPos(fileref,fsfromstart,offset_to_typelist+2);
  2287.       if err<>noerr then goto 99;} {read from mark}
  2288.       bcount:=lsize;
  2289.       scsi_wait;
  2290.       err:=FsRead(fileref,bcount,Ptr(typelist^));
  2291.       hunlock(handle(typelist));     
  2292.       if err<>noerr then goto 99;
  2293.       if bcount<>lsize then goto 98;
  2294.       
  2295.    end;{with mypath}
  2296.  
  2297. openpath:=noerr;
  2298. exit;{normal exit} 
  2299. 98:err:=myerr;
  2300. 99:{error exit}
  2301. closepath(mypath);
  2302. openpath:=err;
  2303. end;{proc openpath}
  2304.  
  2305.  
  2306.  
  2307. {$S        }
  2308. function Mygrowzone(cbneeded:size):longint;
  2309. var dontmove:handle;
  2310.     result:longint;
  2311. begin
  2312.   result:=0;
  2313.   dontmove:=GZsaveHnd;
  2314.   if growzoneguardblock<>nil then
  2315.      if growzoneguardblock<>dontmove then
  2316.       begin
  2317.       result:=GetHandleSize(growzoneguardblock);
  2318.       DisposHandle(growzoneguardblock);
  2319.       growzoneguardblock:=nil;
  2320.       sysbeep(1);
  2321.       end;
  2322.  
  2323. lowmemoryGZflag:=true;
  2324.  
  2325. mygrowzone:=result;
  2326. end;{function}
  2327. procedure setup_mygrowzone;
  2328.  
  2329. begin
  2330. lowmemoryGZflag:=false;
  2331. growzoneguardblock:=newhandle(GZguardblocksize);
  2332. SetGrowZone(@mygrowzone);
  2333.  
  2334. end;
  2335. procedure low_memory_halt;
  2336. {low memory warning - post message in a different way to work better}
  2337. var
  2338.     wait,endit:longint;
  2339. begin
  2340. if rinfo<>nil then 
  2341.   begin
  2342.        DisposPtr(ptr(rinfo));
  2343.        rinfo:=nil;
  2344.    end;
  2345. if ainfo<>nil then 
  2346.   begin
  2347.        DisposPtr(ptr(ainfo));
  2348.        ainfo:=nil;
  2349.    end;
  2350. replaceline('Not enough memory to continue safely',errorline);
  2351. showstatus;
  2352. wait:=120;
  2353. delay(wait,endit);
  2354. close_all_and_halt(true);
  2355. end;
  2356.  
  2357. {$S startup}
  2358. procedure allocate_big_memory;
  2359. const 
  2360.     safety=150000;{minimum free space}
  2361. var needed:size;
  2362.     wevegot,grow:size;
  2363. begin
  2364. needed:=Sizeof(resourceinfoarray)+Sizeof(applinfoarray);
  2365. ResrvMem(needed+safety);
  2366. Wevegot:=MaxMem(grow);
  2367. if wevegot<(needed+safety) then
  2368.      begin
  2369.        low_memory_halt;
  2370.      end
  2371.  else
  2372.      begin
  2373.        rinfo:=resourceinfoarrayptr(NewPtr(needed));
  2374.        ainfo:=applinfoarrayptr(NewPtr(needed));
  2375.        if (rinfo=nil) or(ainfo=nil) then low_memory_halt;
  2376.      end;
  2377.  
  2378. end;
  2379.  
  2380. procedure add_safekey(ss:str255);
  2381. var i:integer;
  2382. begin
  2383. uprString(ss,true);{upper case}
  2384. if safekeywords_count<maxsafekeywords then 
  2385.    begin
  2386.      for i:=1 to safekeywords_count do
  2387.         begin
  2388.           if safekeywords[i]=ss then exit;{already in the list}
  2389.         end;
  2390.      safekeywords_count:=safekeywords_count+1;
  2391.      safekeywords[safekeywords_count]:=ss;
  2392.    end;
  2393. end;{procedure}
  2394.  
  2395. procedure write_safekeys;
  2396. var i:integer;
  2397. begin
  2398. if not outputopen then exit;
  2399.  for i:=1 to safekeywords_count do
  2400.    begin
  2401.    scsi_wait;
  2402.    writeln(outfile,safekeywords[i]);
  2403.    end;
  2404.    write_end_flag('end safe names');
  2405.   
  2406. end;
  2407.  
  2408. procedure read_safekeys;
  2409. {read list of safe keyword file exclusions}
  2410. var line:str255;
  2411.     tokens:tokenstype;
  2412.     ntokens:integer;
  2413. begin
  2414. if not inputopen then exit;
  2415. line:='';
  2416. while not eof(infile) do
  2417.    begin
  2418.    read_input(line);
  2419.    if test_end_flag(line) then exit;
  2420.    tabscan(line,tokens,ntokens);
  2421.    if ntokens>=1 then add_safekey(tokens[1]);
  2422.     end;
  2423. end;
  2424.  
  2425. procedure write_morechecks;
  2426. {write list of additional (boot block?) checksums
  2427.  shell procedure for future expansion}
  2428. var i:integer;
  2429. begin
  2430. if not outputopen then exit;
  2431.   (* write it here *)
  2432.    write_end_flag('end morechecks');
  2433. end;
  2434.  
  2435. procedure read_morechecks;
  2436. {read list of additional (boot block?) checksums
  2437.  shell procedure for future expansion}
  2438. var line:str255;
  2439.     tokens:tokenstype;
  2440.     ntokens:integer;
  2441. begin
  2442. if not inputopen then exit;
  2443. line:='';
  2444. while not eof(infile) do
  2445.    begin
  2446.    read_input(line);
  2447.    if test_end_flag(line) then exit;
  2448.     (* process here *)
  2449.     end;
  2450. end;
  2451.  
  2452. {$S core}
  2453. function filenamesafetylevel(name:str255):safetype;
  2454. {checksum all resources above this level}
  2455. label 99;
  2456. var i:integer;
  2457.     result:safetype;
  2458. begin
  2459. uprString(name,true);{upper case}
  2460.  for i:=1 to safekeywords_count do
  2461.    begin
  2462.    if Pos(safekeywords[i],name)<>0 then
  2463.       begin
  2464.          result:=unknown;
  2465.          goto 99;
  2466.       end;
  2467.    end;   
  2468. result:=safe;
  2469. 99:
  2470. {poststatus(safetynames[result],errorline);}
  2471. filenamesafetylevel:=result;
  2472. end;
  2473.  
  2474. {$S startup}
  2475. procedure start_safekey;
  2476. {Make list of substrings in file names that 
  2477.  indicate the file is a temporary file or settings file
  2478.  that frequently changes and may contain 
  2479.  but "unknown" resource types that are really safe}
  2480. begin
  2481. safekeywords_count:=0;
  2482. add_safekey('Scrapbook');
  2483. add_safekey('Clipboard');
  2484. add_safekey('TEMP');
  2485. add_safekey('WORK');
  2486. add_safekey('SETTING');
  2487. add_safekey('RESUME');
  2488. add_safekey('PREFER');
  2489. add_safekey('OPTION');
  2490. add_safekey('SCRATCH');
  2491. end;
  2492. {$S startup}
  2493. FUNCTION HFSExists: BOOLEAN;
  2494.  {From Tech note #77}
  2495.  var w:wordptr;
  2496. Begin {HFSExists}
  2497.   w:=WordPtr(Pointer(FSFCBLen));
  2498.         HFSExists := (w^) > 0;
  2499. End;  {HFSExists}
  2500.  
  2501. procedure HFSWarning;
  2502. {Quit if HFS not available}
  2503. var wait,endit:longint;
  2504. begin
  2505. if not HFSexists then
  2506.     begin
  2507.     wait:=180;
  2508.     Poststatus('This program requires the HFS file system',errorline);
  2509.     delay(wait,endit);
  2510.     doevent(false);
  2511.     close_all_and_halt(true);
  2512.     end;
  2513. end; {HFSWarning}
  2514.  
  2515. FUNCTION GetRealBootDrive: INTEGER;
  2516.  {From Tech Note #77}
  2517.     VAR
  2518.          MyHPB         : HParamBlockRec;
  2519.          MyWDPB        : WDPBRec;
  2520.          err         : OSErr;
  2521.    w:wordptr;
  2522.         sysVRef    : integer; {will be the vRefNum of open system’s vol}
  2523.  
  2524.     Begin {GetRealBootDrive}
  2525.             if HFSExists then Begin        {If we’re running under HFS... }
  2526.                     
  2527.             {get the VRefNum of the volume that }
  2528.             {contains the open System File      }
  2529.     w:=WordPtr(Pointer(SysMap));
  2530.             err:= GetVRefNum(w^,sysVRef);
  2531.  
  2532.             with MyHPB do Begin          
  2533.             {Get the “System” vRefNum and “Blessed” dirID}
  2534.                             ioNamePtr   := NIL;   
  2535.                             ioVRefNum   := sysVRef; {from the GetVrefNum call}
  2536.                             ioVolIndex  := 0;
  2537.                     End; {with}
  2538.                     err := PBHGetVInfo(@MyHPB, FALSE);
  2539.  
  2540.  
  2541.                    with myWDPB do Begin      {Open a working directory there}
  2542.                             ioNamePtr   := NIL;
  2543.                             ioVRefNum   := sysVRef;
  2544.                        ioWDProcID  := SysWDProcID; {Using the system proc ID}
  2545.                             ioWDDirID   := myHPB.ioVFndrInfo[1];{ see TechNote 67}
  2546.                     End; {with}
  2547.             err := PBOpenWD(@myWDPB, FALSE);
  2548.  
  2549.                     GetRealBootDrive := myWDPB.ioVRefNum;
  2550.             {We’ve got the real WD}
  2551.             End Else {we’re running MFS}
  2552.                  begin
  2553.                  w:=WordPtr(Pointer(BootDrive));
  2554.                     GetRealBootDrive := w^; 
  2555.                  end;
  2556.             {BootDrive is valid under MFS}
  2557. End;  {GetRealBootDrive}
  2558.  
  2559. {$S core}
  2560. procedure tabscan{(line:str255; var tokens:tokenstype;var ntokens:integer)};
  2561. {Input scanner: breaks a line into tokens separated by tabs}
  2562. {Trims leading and trailing blanks}
  2563. label 99,88;
  2564. var tab,sp:char;
  2565.     i,j,next,last:integer;
  2566. begin
  2567. tab:=chr(9);
  2568. sp:=' ';
  2569.  
  2570. for i:=1 to maxtokens do tokens[i]:='';
  2571.  
  2572. ntokens:=0;
  2573. next:=1;
  2574. last:=length(line);
  2575. while(next<=last) do
  2576.     begin
  2577.     {skip leading blanks}
  2578.     while(line[next]=sp) do
  2579.         begin
  2580.            next:=next+1;
  2581.            if(next>last) then goto 99;
  2582.         end;{while not space}
  2583.         
  2584.     {copy up to tab or end of line}
  2585.     ntokens:=ntokens+1;
  2586.     while(line[next]<>tab) do
  2587.         begin
  2588.            tokens[ntokens]:=concat(tokens[ntokens],line[next]);
  2589.            next:=next+1;
  2590.            if(next>last)then goto 99;
  2591.         end;{while not tab}
  2592.     next:=next+1;{skip tab}
  2593.     end;{while}
  2594. 99:
  2595. {remove trailing spaces}
  2596. for i:=1 to ntokens do
  2597.   begin
  2598.      last:=length(tokens[i]);
  2599.      for j:=length(tokens[i]) downto 1 do
  2600.         begin
  2601.         if tokens[i][j]<>sp then
  2602.             begin
  2603.                last:=j;
  2604.                goto 88;
  2605.             end;
  2606.         end;
  2607.      88:
  2608.        if last>0 then 
  2609.              tokens[i]:=copy(tokens[i],1,last)
  2610.           else
  2611.              tokens[i]:='';
  2612.   end;{for i}
  2613. end;{proc}
  2614.  
  2615.  
  2616. function find_type(atype:restype):integer;
  2617. {find a resource type if it exists.  Return the current supscript or zero
  2618. if it does not exist}
  2619. {binary search}
  2620. var low,high,mid:integer;
  2621. begin
  2622. low:=1;
  2623. high:=rtypes_count;
  2624.    while low<=high do
  2625.      begin
  2626.        mid:=(low+high) div 2;
  2627.        if atype=rtypes[mid].thetype then
  2628.            begin
  2629.              find_type:=mid;
  2630.               exit;
  2631.            end
  2632.         else if atype>rtypes[mid].thetype then
  2633.            begin
  2634.              low:=mid+1;
  2635.            end
  2636.         else
  2637.            begin
  2638.               high:=mid-1;
  2639.            end;
  2640.       end;{while}
  2641. find_type:=0;{no match}
  2642. end;{function}
  2643.  
  2644. function find_and_add_type(atype:restype;howsafe:safetype):integer;
  2645. {find a resource type if it exists, otherwise add it to rtypes in sorted
  2646.  order.  Return the current supscript}
  2647. {binary search and insertion}
  2648. var low,high,mid,ii,at:integer;
  2649.      ss:str255;
  2650. begin
  2651. low:=1;
  2652. high:=rtypes_count;
  2653. at:=low;
  2654.    while low<=high do
  2655.      begin
  2656.        mid:=(low+high) div 2;
  2657.        if atype=rtypes[mid].thetype then
  2658.            begin
  2659.              find_and_add_type:=mid;
  2660.               exit;
  2661.            end
  2662.         else if atype>rtypes[mid].thetype then
  2663.            begin
  2664.              low:=mid+1;
  2665.              at:=mid+1;
  2666.            end
  2667.         else  {atype<rtypes[mid].thetype}
  2668.            begin
  2669.               high:=mid-1;
  2670.               at:=mid;
  2671.            end;
  2672.      
  2673.       end;{while}
  2674.  
  2675. if rtypes_count>=maxtype then
  2676.      begin
  2677.        poststatus('My maximum resource type count exceeded',errorline);
  2678.        find_and_add_type:=0;
  2679.        exit;
  2680.     end;
  2681. for ii:=rtypes_count downto at do
  2682.     begin
  2683.     rtypes[ii+1]:=rtypes[ii]
  2684.     end;
  2685. rtypes[at].thetype:=atype;
  2686. rtypes[at].safety:=howsafe;
  2687. rtypes[at].occurs:=0;
  2688. {wait_for_buttons(concat(atype,safetynames[howsafe],continuebut));}
  2689.  
  2690. rtypes_count:=rtypes_count+1;    
  2691. find_and_add_type:=at;
  2692. end;{function}
  2693.  
  2694. function find_type_old(atype:restype):integer;
  2695. {old version}
  2696. label 99;
  2697. var i:integer;
  2698.     result:integer;
  2699. begin
  2700. result:=0;
  2701.   for i:=1 to rtypes_count do
  2702.      begin
  2703.        with rtypes[i] do
  2704.           begin
  2705.             if thetype=atype then
  2706.                begin
  2707.                   result:=i;
  2708.                   goto 99;
  2709.                end;
  2710.           end;
  2711.      end;
  2712. 99:
  2713. find_type_old:=result;
  2714.  
  2715. end;{find_type}
  2716.  
  2717. procedure add_type(atype:restype;howsafe:safetype);
  2718. {add a type and it's classification to the list in memory
  2719. if it does not already exist}
  2720. var ignore:integer;
  2721. begin
  2722. ignore:=find_and_add_type(atype,howsafe);
  2723. end;
  2724.  
  2725. procedure add_type_old(atype:restype;howsafe:safetype);
  2726. var i,result:integer;
  2727. {add type to tables if it does not already exist}
  2728. {old version}
  2729. begin
  2730. if find_type(atype)=0 then
  2731.     begin
  2732.     if rtypes_count<maxtype then
  2733.         begin
  2734.          rtypes_count:=rtypes_count+1;
  2735.          with rtypes[rtypes_count] do
  2736.            begin
  2737.               thetype:=atype;
  2738.               safety:=howsafe;
  2739.               occurs:=0;
  2740.            end ;{with}
  2741.            sorttypes(rtypes,rtypes_count);
  2742.          end
  2743.      else
  2744.          begin
  2745.            poststatus('My maximum resource type count exceeded',errorline);
  2746.          end;
  2747.          
  2748.      end; 
  2749.  
  2750. end;{add_type}
  2751.  
  2752. {$S appl}
  2753. procedure detail_appl_check;
  2754. {Compare the checksums in memory with the old checksums on file for applications}
  2755. {This assumes both are sorted by creator,creationdate,filename,dirid}
  2756. {if no checksums were done before, just compare sizes}
  2757.  
  2758. type statetype=(oldgreater,newgreater,
  2759.                checkequality,sizeequality,
  2760.                sameappl,sameapplbadsize,sameapplbadcheck);
  2761. var 
  2762.     jnew:integer;
  2763.     jcreator:ostype;
  2764.     jcreatorstart:integer;
  2765.     jcreatorend:integer;
  2766.     state:statetype;
  2767.     oldfile,newfile:myfilenametype;
  2768.     filecomp:integer;
  2769.     oldcreator,newcreator:OStype;
  2770.     oldcreationdate,newcreationdate:longint;
  2771.     oldsize,newsize:longint;
  2772.     oldunsafecount,newunsafecount:longint;
  2773.     oldchecksize,newchecksize:longint;
  2774.     oldchecksum,newchecksum:integer;
  2775.     olddirid,newdirid:longint;
  2776.     oldvol,newvol:integer;
  2777.     newhidden:boolean;
  2778.     end_on_old,end_on_new:boolean;
  2779.     oid,nid:str255;
  2780.  
  2781. procedure get_next_old;
  2782. label 22;{repeat}
  2783. var line:str255;
  2784.     tokens:tokenstype;
  2785.     ntokens:integer;
  2786.     work:longint;
  2787. begin
  2788. 22:
  2789. ntokens:=0;
  2790. repeat
  2791. if end_on_old then exit; 
  2792. if eof(infile) then
  2793.     begin
  2794.       end_on_old:=true;
  2795.       exit;
  2796.     end;
  2797. read_input(line);
  2798. {treat "*****" as end}
  2799. if test_end_flag(line) then
  2800.     begin
  2801.       end_on_old:=true;
  2802.       exit;
  2803.     end;
  2804. tabscan(line,tokens,ntokens)
  2805.  
  2806. until(ntokens>=9);{ignore short/blank lines}
  2807.  
  2808. {breakdown line as:}
  2809. {creator <tab> creationdate <tab> filename <tab> dirid <tab> volume
  2810. <tab> thesize <tab> unsafecount <tab> checksize <tab> checksum}
  2811.  
  2812. filltype(oldcreator,tokens[1]);{blank fill type}
  2813. stringtonum(tokens[2],oldcreationdate);
  2814. oldfile:=tokens[3];
  2815. stringtonum(tokens[4],olddirid);
  2816. stringtonum(tokens[5],work);
  2817. oldvol:=work;
  2818. stringtonum(tokens[6],oldsize);
  2819. stringtonum(tokens[7],work);
  2820. oldunsafecount:=work;
  2821. stringtonum(tokens[8],oldchecksize);
  2822. stringtonum(tokens[9],work);
  2823. oldchecksum:=work;
  2824. {additional stuff at end of line will be ignored}
  2825. {skip non-matching volumes}
  2826. if oldvol<>0 then
  2827.    if oldvols[oldvol].matchto=0 then goto 22;
  2828. end;{get_next_old}
  2829.  
  2830. procedure get_next_new;
  2831. label 88,22;
  2832. var jj:integer;
  2833. begin
  2834. 22:
  2835. jnew:=jnew+1;
  2836. if jnew>acount then 
  2837.     begin
  2838.       end_on_new:=true;
  2839.       exit;
  2840.     end;
  2841.     (*
  2842.           {info on applications}
  2843.       applinforec=record
  2844.                   thesize:longint;
  2845.                   creator:OStype;
  2846.                   creationdate:longint;
  2847.                   dirid:longint;
  2848.                   filename:myfilenametype;
  2849.                   unsafecount:integer;
  2850.                   checksum:integer;
  2851.                   checksize:longint;
  2852.                   flags:integer;
  2853.                   end;
  2854.  
  2855.     *)
  2856. with  ainfo^[jnew] do
  2857.    begin
  2858.      newcreator:=creator;
  2859.      newcreationdate:=creationdate;
  2860.      newfile:=filename;
  2861.      newsize:=thesize;
  2862.      newunsafecount:=unsafecount;
  2863.      newchecksize:=checksize;
  2864.      newchecksum:=checksum;
  2865.      newdirid:=dirid;
  2866.      {matching old volume if any}
  2867.      if (flags and applvolumemask)=0 then
  2868.               begin
  2869.               newvol:=0
  2870.               end
  2871.      else
  2872.               begin
  2873.               newvol:=newvols[(flags and applvolumemask)].matchto;
  2874.               {skip non-matching volumes}
  2875.               if newvol=0 then goto 22;
  2876.               end;
  2877.          
  2878.      newhidden:=(flags and applinvisiblemask)<>0;
  2879.    end;{with}
  2880.  
  2881. {find last occurance of current signature}   
  2882. if newcreator<>jcreator then
  2883.     begin
  2884.     jcreator:=newcreator;
  2885.     jcreatorstart:=jnew;
  2886.     for jj:=jnew+1 to acount do
  2887.        if ainfo^[jj].creator<>jcreator then
  2888.          begin
  2889.            jcreatorend:=jj-1;
  2890.            goto 88;
  2891.          end;
  2892.      jcreatorend:=acount;
  2893.      88:
  2894.     end;
  2895.  
  2896. end;{get_next_new}
  2897.  
  2898. procedure was_renamed_or_moved;
  2899. label 88;
  2900. {one to many check for current signature}
  2901. {call this if no match and there is more than one occurance}
  2902. {this assumes that an application is OK regardless of name and
  2903.  directory changes if other features match:
  2904.         creationdate
  2905.         checksize
  2906.         checksum
  2907.         unsafecount
  2908.         
  2909.         or if (oldunsafecount=notcounted)           
  2910.            creationdate
  2911.            thesize}
  2912. var 
  2913.     jj:integer;
  2914. begin
  2915. for jj:=jcreatorstart to jcreatorend do
  2916.     with ainfo^[jj] do
  2917.     begin
  2918.       if creationdate=oldcreationdate then
  2919.           if (oldunsafecount=notcounted) or (fastapplcheck) then
  2920.              begin
  2921.              if (oldsize=thesize) then 
  2922.                   begin
  2923.                   flags:=flags or applrenamemask;
  2924.                   end
  2925.              end
  2926.           else
  2927.              begin
  2928.              if oldunsafecount=unsafecount then
  2929.                if oldchecksize=checksize then
  2930.                  if oldchecksum=checksum then;
  2931.                  begin
  2932.                   flags:=flags or applrenamemask;
  2933.                  end;
  2934.              end;
  2935.     end;
  2936. end;{was_moved_or_renamed}
  2937.  
  2938. begin{detail_resource_check}
  2939. jcreator:='$$$$';
  2940. if not inputopen then exit;
  2941. jnew:=0;
  2942. end_on_old:=false;
  2943. end_on_new:=false;
  2944. get_next_old;
  2945. get_next_new;
  2946. while not(end_on_old or end_on_new) do
  2947.   begin
  2948.   {debug info}
  2949.   (*
  2950.   poststatus(concat(concat(oldfile,':'),newfile),byline);
  2951.   poststatus(concat(concat(oldcreator,':'),newcreator),byline+1); 
  2952.   numtostring(oldcreationdate,oid);
  2953.   numtostring(newcreationdate,nid);
  2954.   poststatus(concat(concat(oid,':'),nid),byline+2);
  2955.   numtostring(olddirid,oid);
  2956.   numtostring(newdirid,nid);
  2957.   poststatus(concat(concat(oid,':'),nid),byline+3);
  2958.   *)
  2959.   state:=newgreater;
  2960.   
  2961.   {if creator signature matches, check for rename/move on partial match}
  2962.   if oldcreator=newcreator then
  2963.      begin
  2964.        if oldcreationdate=newcreationdate then
  2965.            begin
  2966.           {wait_for_buttons('creation match',continuebut);}
  2967.            filecomp:=filenamecompare(oldfile,newfile);
  2968.            if filecomp=0{equal} then
  2969.               begin
  2970.                {wait_for_buttons('filename match',continuebut);}
  2971.               if oldvol=newvol then
  2972.                 begin
  2973.                   if olddirid=newdirid then
  2974.                      begin
  2975.                      {wait_for_buttons('dir match',continuebut);}
  2976.                         {this is the same file name in the same directory}
  2977.                         state:=sameappl;
  2978.                         {now check for changed vs exact match}
  2979.                        {match on
  2980.                         checksize
  2981.                         checksum
  2982.                         unsafecount
  2983.                         
  2984.                         or if oldunsafecount=notcounted           
  2985.                            thesize}
  2986.                            
  2987.                         if (oldunsafecount=notcounted) or (fastapplcheck) then
  2988.                            begin
  2989.                            if (oldsize=newsize) or newhidden then
  2990.                               {less stringent check on non-application
  2991.                                hidden files - Desktop changes size}
  2992.                               state:=sizeequality;
  2993.                            end
  2994.                         else
  2995.                            begin
  2996.                            if oldchecksize=newchecksize then
  2997.                            if oldchecksum=newchecksum then
  2998.                            if oldunsafecount=newunsafecount then
  2999.                               state:=checkequality;                       
  3000.                            end;
  3001.                         
  3002.                      end{=dir}
  3003.                   else 
  3004.                      begin
  3005.                      if olddirid>newdirid then
  3006.                          begin
  3007.                          state:=oldgreater;
  3008.                          end;
  3009.                      was_renamed_or_moved;
  3010.                      end{<>dir}
  3011.                 end{=vol}
  3012.               else
  3013.                  begin
  3014.                      if oldvol>newvol then
  3015.                          begin
  3016.                          state:=oldgreater;
  3017.                          end;
  3018.                      was_renamed_or_moved;
  3019.                  end{<>vol}
  3020.               end{=file}
  3021.            else
  3022.               begin
  3023.               if filecomp>0{oldfile>newfile} then
  3024.                  begin
  3025.                    state:=oldgreater;
  3026.                  end;
  3027.                  was_renamed_or_moved;
  3028.               end{<>file}
  3029.            
  3030.            end{=creation date}
  3031.        else
  3032.            begin
  3033.            if oldcreationdate>newcreationdate then
  3034.               begin
  3035.                  state:=oldgreater;
  3036.               end;
  3037.               was_renamed_or_moved;
  3038.            end{<>creation date}
  3039.            
  3040.      end{= creator}
  3041.   else if oldcreator>newcreator then
  3042.       begin
  3043.          state:=oldgreater;
  3044.       end;
  3045.   
  3046.     
  3047.    {end of compares}     
  3048.     case state of
  3049.     sizeequality:  
  3050.                  begin
  3051.                  {equality on the basis of size - fast check}
  3052.                  ainfo^[jnew].flags:=ainfo^[jnew].flags or applexactmatchmask;
  3053.                  get_next_old;
  3054.                  get_next_new;
  3055.                  {wait_for_buttons('equal appl',continuebut);}
  3056.                  if fastapplcheck and (oldunsafecount<>notcounted) then
  3057.                       begin
  3058.                          {copy old checksums etc. 
  3059.                           for future reference so that info
  3060.                           is not lost by writing output from
  3061.                           a short check}
  3062.                          with ainfo^[jnew] do
  3063.                            begin
  3064.                            checksum:=oldchecksum;
  3065.                            unsafecount:=oldunsafecount;
  3066.                            checksize:=oldchecksize;
  3067.                            end;
  3068.                       end;             
  3069.                end;
  3070.     checkequality:  begin
  3071.                  {equality on the basis of all checksums - full check}
  3072.                  ainfo^[jnew].flags:=ainfo^[jnew].flags or applexactmatchmask;
  3073.                  get_next_old;
  3074.                  get_next_new;
  3075.                  {wait_for_buttons('equal appl',continuebut);}             
  3076.                end;
  3077.     sameappl:   begin
  3078.                  ainfo^[jnew].flags:=ainfo^[jnew].flags or applchangedmask;
  3079.                   {wait_for_buttons('same appl no match',continuebut);}
  3080.                 get_next_old; 
  3081.                 get_next_new;             
  3082.                end;
  3083.     sameapplbadsize:   begin
  3084.                  ainfo^[jnew].flags:=ainfo^[jnew].flags or applchangedmask;
  3085.                  ainfo^[jnew].flags:=ainfo^[jnew].flags or applbadsizemask;
  3086.                   {wait_for_buttons('same appl no match',continuebut);}
  3087.                 get_next_old; 
  3088.                 get_next_new;             
  3089.                end;
  3090.     sameapplbadcheck:   begin
  3091.                  ainfo^[jnew].flags:=ainfo^[jnew].flags or applchangedmask;
  3092.                  ainfo^[jnew].flags:=ainfo^[jnew].flags or applbadcheckmask;
  3093.                   {wait_for_buttons('same appl no match',continuebut);}
  3094.                 get_next_old; 
  3095.                 get_next_new;             
  3096.                end;
  3097.     oldgreater:begin
  3098.                  {wait_for_buttons('old greater',continuebut);}
  3099.                  get_next_new;                 
  3100.                end;
  3101.     newgreater:begin
  3102.                  {wait_for_buttons('new greater',continuebut);}
  3103.                  get_next_old;
  3104.                end;
  3105.     end;{case of state}
  3106.     
  3107.   end;{while not done}
  3108.  
  3109. end;
  3110. procedure show_APPL_detail_changes;
  3111. {On Screen Summary of Application Changes}
  3112. label 77;
  3113. const
  3114.     chlimit=10;
  3115. var 
  3116.     j:integer;
  3117.     charray:array[1..chlimit] of integer;
  3118.     ch,chcount:integer;
  3119.     filename,dname:str255;
  3120.     work:str255;
  3121.     jtype:integer;
  3122.     dd:longint;
  3123.     newappl:longint;
  3124.     moveorrenamedappl:longint;
  3125.     changedappl:longint;
  3126.     dangerappl:longint;
  3127.     ans_show,notify:boolean;
  3128.     hidden:boolean;
  3129.     vnum:integer;
  3130. procedure show_change(mess:str255);
  3131. begin
  3132. folder_name(dd,dname);
  3133. clear_to_end(fileline);
  3134. PostStatus(newvols[vnum].vname,fileline);
  3135. work:= concat(concat(dname,':'),filename);
  3136. poststatus(work,fileline+1);
  3137.  
  3138. wait_for_buttons(mess,continuebut);
  3139. clear_to_end(fileline);
  3140. end;
  3141.  
  3142. procedure mark_change(jj:integer);
  3143. begin
  3144. notify:=true;
  3145. {keep pointers to the first few changes to speed up display}
  3146. if chcount<chlimit then
  3147.       begin 
  3148.        chcount:=chcount+1;
  3149.        charray[chcount]:=jj;
  3150.       end;
  3151. end;
  3152.  
  3153. begin
  3154. chcount:=0;
  3155. {skip this all if there was no input file}
  3156. if not inputopen then exit;
  3157. {count changes}
  3158. newappl:=0;
  3159. moveorrenamedappl:=0;
  3160. changedappl:=0;
  3161. dangerappl:=0;
  3162. notify:=false;
  3163. for j:=1 to acount do
  3164.   begin
  3165.   {only flag changes to stuff on matched volumes}
  3166.   if newvols[(ainfo^[j].flags and applvolumemask)].matchto<>0 then
  3167.     begin
  3168.        if (ainfo^[j].flags and applexactmatchmask)<>applexactmatchmask then
  3169.          if (ainfo^[j].flags and applrenamemask)=applrenamemask then
  3170.             begin
  3171.                {moved or renamed or duplicated}
  3172.                moveorrenamedappl:=moveorrenamedappl+1;
  3173.                mark_change(j);
  3174.             end
  3175.          else
  3176.             begin
  3177.               if (ainfo^[j].flags and applchangedmask)=applchangedmask then
  3178.                 begin
  3179.                   {changed}
  3180.                   changedappl:=changedappl+1;
  3181.                   mark_change(j);
  3182.                 end
  3183.               else
  3184.                 begin
  3185.                    {"new"}
  3186.                     newappl:=newappl+1;
  3187.                     mark_change(j);
  3188.                     
  3189.                 end
  3190.             end;
  3191.      end;
  3192.      if (ainfo^[j].flags and appldangermask)=appldangermask then
  3193.          begin
  3194.          dangerappl:=dangerappl+1;
  3195.          mark_change(j);
  3196.          end;       
  3197.   end;{for}
  3198.  {quick skip to end}
  3199. if chcount<chlimit then
  3200.      begin
  3201.      chcount:=chcount+1;
  3202.      charray[chcount]:=acount+1;
  3203.      end;
  3204.      
  3205. {exit if no changes}
  3206. if not notify then exit;
  3207.  
  3208. {notify of changes and give a chance to see the changes on screen and on disk}
  3209. sysbeep(1);
  3210. clear_to_end(askline-3);
  3211. poststatus('',askline-4);
  3212. numtostring(newappl,work);
  3213. work:=concat('New: ',work);
  3214. poststatus(work,askline-4);
  3215. numtostring(moveorrenamedappl,work);
  3216. work:=concat('Renamed/Moved: ',work);
  3217. poststatus(work,askline-3);
  3218. numtostring(changedappl,work);
  3219. work:=concat('Changed: ',work);
  3220. poststatus(work,askline-2);
  3221.  
  3222. if Dangerappl<>0 then
  3223. begin
  3224. numtostring(dangerappl,work);
  3225. work:=concat('Dangerous: ',work);
  3226. poststatus(work,askline-1);
  3227. end;
  3228.  
  3229. work:=
  3230. 'These are differences in the applications/hiddenfiles.  Do you want to see the details on screen?';
  3231. ans_show:=ask(work,nodefaultbut);
  3232. clear_to_end(askline-4);
  3233.  
  3234. if not outputopen then
  3235.    if ask('Do you want to write a new summary output file?',nodefaultbut) then 
  3236.       begin
  3237.        open_output;      
  3238.       end;
  3239.       
  3240. if not ans_show then exit;
  3241. poststatus('List differences in applications or hidden files:',pathline);
  3242. {loop to show individual changes}
  3243. j:=0;
  3244. ch:=0;
  3245. while(j<=acount)do 
  3246.   begin
  3247.   {faster skip to marked changes}
  3248.   if ch<chcount then
  3249.      begin
  3250.        ch:=ch+1;
  3251.        j:=charray[ch];
  3252.      end
  3253.   else
  3254.      begin
  3255.        j:=j+1;
  3256.      end;
  3257.     if j>acount then goto 77;
  3258.     
  3259.     filename:=ainfo^[j].filename;
  3260.     dd:=ainfo^[j].dirid;
  3261.     vnum:=ainfo^[j].flags and applvolumemask;
  3262.     hidden:=(ainfo^[j].flags and applinvisiblemask)<>0;
  3263.     if newvols[(ainfo^[j].flags and applvolumemask)].matchto<>0 then
  3264.         begin
  3265.         if (ainfo^[j].flags and applexactmatchmask)<>applexactmatchmask then
  3266.           if (ainfo^[j].flags and applrenamemask)=applrenamemask then
  3267.              begin
  3268.                 {moved or renamed or duplicated}
  3269.                 Show_change('was moved, renamed or duplicated')
  3270.              end
  3271.           else
  3272.              begin
  3273.                if (ainfo^[j].flags and applchangedmask)=applchangedmask then
  3274.                  begin
  3275.                    {changed}
  3276.                    if hidden then
  3277.                      Show_change('Changed Invisible File')
  3278.                    else 
  3279.                      Show_change('Changed Application');
  3280.                  end
  3281.                else
  3282.                  begin
  3283.                    if hidden then
  3284.                      Show_change('New Invisible File')
  3285.                    else 
  3286.                      Show_change('New Application');
  3287.                  end
  3288.              end;
  3289.         end;
  3290.    if (ainfo^[j].flags and appldangermask)=appldangermask then
  3291.        begin
  3292.        Show_change('Infected with a dangerous resource type');
  3293.        end;
  3294.   end;{for}
  3295. 77:
  3296. clear_to_end(pathline);
  3297. end;{proc show_APPL_detail_changes}
  3298.  
  3299. {$S detail}
  3300. procedure detail_resource_check;
  3301. {Compare the checksums in memory with the old checksums on file}
  3302. {This assumes both are sorted by filename,type,id}
  3303. {If there is more than one entry per id the old file is
  3304.  assumed sorted by filename,type,id,size,name and checksum.
  3305.  This is to allow a old checksum file valid for two system
  3306.  versions to be constructed, but this feature is not fully
  3307.  supported}
  3308.  
  3309. type statetype=(oldgreater,newgreater,equality,sameid);
  3310. var 
  3311.     jnew:integer;
  3312.     state:statetype;
  3313.     oldtype,newtype:restype;
  3314.     oldfile,newfile:myfilenametype;
  3315.     oldid,newid:integer;
  3316.     oldsize,newsize:longint;
  3317.     oldchecksum,newchecksum:integer;
  3318.     oldname,newname:string[10];
  3319.     end_on_old,end_on_new:boolean;
  3320.     oid,nid:str255;
  3321.     filecomp:integer;
  3322.  
  3323. procedure get_next_old;
  3324. var line:str255;
  3325.     tokens:tokenstype;
  3326.     ntokens:integer;
  3327.     work:longint;
  3328. begin
  3329. ntokens:=0;
  3330. repeat
  3331. if end_on_old then exit; 
  3332. if eof(infile) then
  3333.     begin
  3334.       end_on_old:=true;
  3335.       exit;
  3336.     end;
  3337. read_input(line);
  3338. {treat "*****" as end}
  3339. if test_end_flag(line) then
  3340.     begin
  3341.       end_on_old:=true;
  3342.       exit;
  3343.     end;
  3344. tabscan(line,tokens,ntokens)
  3345.  
  3346. until(ntokens>=6);{ignore short/blank lines}
  3347.  
  3348. {breakdown line as:}
  3349. {type <tab> id <tab> size <tab> checksum <tab> name <tab> filename}
  3350. filltype(oldtype,tokens[1]);{blank fill type}
  3351. stringtonum(tokens[2],work);
  3352. oldid:=work;
  3353. stringtonum(tokens[3],oldsize);
  3354. stringtonum(tokens[4],work);
  3355. oldchecksum:=work;
  3356. oldname:=tokens[5];
  3357. oldfile:=tokens[6];
  3358. {additional stuff at end of line will be ignored}
  3359.  
  3360. end;{get_next_old}
  3361.  
  3362. procedure get_next_new;
  3363.  
  3364. begin
  3365. jnew:=jnew+1;
  3366. if jnew>rcount then 
  3367.     begin
  3368.       end_on_new:=true;
  3369.       exit;
  3370.     end;
  3371. with  rinfo^[jnew] do
  3372.    begin
  3373.      newtype:=thetype;
  3374.      newfile:=sysfiles[(filenameindex and fnamemask)];
  3375.      newid:=theid;
  3376.      newsize:=thesize;
  3377.      newchecksum:=checksum;
  3378.      newname:=thename;  
  3379.    end;
  3380.  
  3381. end;{get_next_new}
  3382.  
  3383. begin{detail_resource_check}
  3384. if not inputopen then exit;
  3385. poststatus('Compare System Folder Resources',fileline);
  3386. jnew:=0;
  3387. end_on_old:=false;
  3388. end_on_new:=false;
  3389. get_next_old;
  3390. get_next_new;
  3391. while not(end_on_old or end_on_new) do
  3392.   begin
  3393.   {debug info}
  3394.  (* poststatus(concat(concat(oldfile,':'),newfile),pathline);
  3395.   poststatus(concat(concat(oldtype,':'),newtype),pathline+1); 
  3396.   numtostring(oldid,oid);
  3397.   numtostring(newid,nid);
  3398.   poststatus(concat(concat(oid,':'),nid),pathline+2);*)
  3399.   state:=newgreater;
  3400.   filecomp:=filenamecompare(oldfile,newfile);
  3401.   if filecomp=0{oldfile=newfile} then
  3402.     begin
  3403.     if oldtype=newtype then
  3404.       begin
  3405.       if oldid=newid then
  3406.         begin
  3407.         
  3408.         {---mark new as id level match---
  3409.          since id's are unique this can be used to distingush
  3410.          added resources from changed resources}
  3411.          
  3412.         state:=sameid;
  3413.         rinfo^[jnew].filenameindex:=(rinfo^[jnew].filenameindex or idmatchmask);
  3414.  
  3415.         if oldsize=newsize then
  3416.           begin
  3417.           if oldname=newname then
  3418.             begin
  3419.             if oldchecksum=newchecksum then
  3420.               begin
  3421.               
  3422.               {---mark exact equality---}
  3423.               
  3424.               state:=equality;
  3425.               rinfo^[jnew].filenameindex:=(rinfo^[jnew].filenameindex or exactmatchmask);
  3426.  
  3427.               end{checksum equal}
  3428.             end{name equal}
  3429.           end{size equal}
  3430.         end{id equal}
  3431.         
  3432.       else if oldid>newid then
  3433.         state:=oldgreater;
  3434.       end{type equal}
  3435.       
  3436.     else if oldtype>newtype then
  3437.       state:=oldgreater;
  3438.     end{file equal}
  3439.     
  3440.   else if filecomp>0{oldfile>newfile} then
  3441.     state:=oldgreater;
  3442.     
  3443.    {end of compares}     
  3444.     case state of
  3445.     equality:  begin
  3446.                  get_next_old;
  3447.                  get_next_new;              
  3448.                end;
  3449.     sameid:    begin
  3450.                  {in case the old file has multiple entries for the same id}
  3451.                  { wait_for_buttons('same id no match',continuebut);}
  3452.                 get_next_old;              
  3453.                end;
  3454.     oldgreater:begin
  3455.                  {wait_for_buttons('old greater',continuebut);}
  3456.                  get_next_new;                 
  3457.                end;
  3458.     newgreater:begin
  3459.                  {wait_for_buttons('new greater',continuebut);}
  3460.                  get_next_old;
  3461.                end;
  3462.     end;{case of state}
  3463.     
  3464.   end;{while not done}
  3465.  
  3466. end;
  3467. procedure show_detail_changes;
  3468. {On Screen Summary of Changes}
  3469. {this will show added or changed resources but not
  3470.  deleted resources}
  3471. var jres:integer;
  3472.     new:boolean;
  3473.     filename:str255;
  3474.     safename:str255;
  3475.     id,work,name:str255;
  3476.     jtype:integer;
  3477.     neworchanged:longint;
  3478.     norc:str255;
  3479.     ans_show:boolean;
  3480. procedure show_change(mess:str255);
  3481. begin
  3482. clear_to_end(fileline);
  3483. work:= concat('File: ',filename);
  3484. poststatus(work,fileline);
  3485. work:=concat(concat('Type:',rinfo^[jres].thetype),' (');
  3486. work:=concat(concat(concat(work,safename),') Id:'),id);
  3487. name:=rinfo^[jres].thename;
  3488. if name<>'' then
  3489.     begin
  3490.         work:=concat(concat(work,' Name:'),name);
  3491.     end;
  3492. poststatus(work,errorline);
  3493. wait_for_buttons(mess,continuebut);
  3494. clear_to_end(fileline);
  3495. end;
  3496. begin
  3497. {skip this all if there was no input file}
  3498. if not inputopen then exit;
  3499. {count changes}
  3500. neworchanged:=0;
  3501. for jres:=1 to rcount do
  3502.   begin
  3503.     if (rinfo^[jres].filenameindex and exactmatchmask)<>exactmatchmask then
  3504.       neworchanged:=neworchanged+1;  
  3505.   end;{for}
  3506.  
  3507. {exit if no changes}
  3508. if neworchanged=0 then exit;
  3509.  
  3510. {notify of changes and give a chance to see the changes on screen and on disk}
  3511. sysbeep(1);
  3512. numtostring(neworchanged,norc);
  3513. norc:=concat(concat('There are ',norc),
  3514. ' new or changed resources in the system folder.  Do you want to see the details on screen?');
  3515. ans_show:=ask(norc,nodefaultbut);
  3516.  
  3517. if not outputopen then
  3518.    if ask('Do you want to write a new summary output file?',nodefaultbut) then 
  3519.       begin
  3520.        open_output;      
  3521.         if writeoutputflag and fastapplcheck then
  3522.         fastapplcheck:=not Ask('Do you want a full checksum of applications',yesbut);    
  3523.       end;
  3524.       
  3525. if not ans_show then exit;
  3526.  
  3527. {loop to show individual changes}
  3528. for jres:=1 to rcount do
  3529.   begin
  3530.  
  3531.     {test flag to see if exact match}
  3532.     if (rinfo^[jres].filenameindex and exactmatchmask)<>exactmatchmask then
  3533.        begin
  3534.          {test flag to see if new or changed}
  3535.          new:=not ((rinfo^[jres].filenameindex and idmatchmask)=idmatchmask);
  3536.          filename:=sysfiles[(rinfo^[jres].filenameindex and fnamemask)];
  3537.          {name for safety level of type}
  3538.          jtype:=find_type(rinfo^[jres].thetype);
  3539.          if jtype=0 then
  3540.             safename:=safetynames[Unknown]
  3541.          else 
  3542.             begin
  3543.                safename:=safetynames[rtypes[jtype].safety];
  3544.             end;
  3545.          numtostring(rinfo^[jres].theid,id);
  3546.          if new then
  3547.               show_change('This resource is new')
  3548.           else
  3549.               show_change('This resource is changed');
  3550.        end
  3551.      else
  3552.        if rtypes[find_type(rinfo^[jres].thetype)].safety=dangerous then
  3553.          begin
  3554.            show_change('This is a dangerous resource type associated with viruses');        
  3555.          end;
  3556.   end;{for}
  3557.  
  3558.  
  3559. end;{proc show_detail_changes}
  3560. {$S startup}
  3561. procedure start_types;
  3562. {set up table of some resource types to allow starting without an
  3563. input file and for testing}
  3564. {See Inside Mac Volume V resource manager for a listing
  3565.  of many types}
  3566. begin
  3567. rtypes_count:=0;
  3568. safetynames[Safe]:='Safe';
  3569. safetynames[Unsafe]:='Unsafe';
  3570. safetynames[Unknown]:='Unknown';
  3571. safetynames[Dangerous]:='Dangerous';
  3572. {resource types associated with known viruses}
  3573. add_type('nVIR',Dangerous);
  3574. {Some RESOURCE TYPES KNOWN TO CONTAIN EXECUTABLE CODE}
  3575. {also include types that occur sometimes in known viruses,sometimes
  3576.  in normal use}
  3577. add_type('CODE',unsafe);
  3578. add_type('INIT',unsafe);
  3579. add_type('ROvr',unsafe);
  3580. add_type('ROv#',unsafe);
  3581. add_type('PTCH',unsafe);
  3582. add_type('PACK',unsafe);
  3583. add_type('PDEF',unsafe);
  3584. add_type('ADBS',unsafe);
  3585. add_type('CACH',unsafe);
  3586. add_type('CDEF',unsafe);
  3587. add_type('cdev',unsafe);
  3588. add_type('DRVR',unsafe);
  3589. add_type('FKEY',unsafe);
  3590. add_type('FMTR',unsafe);
  3591. add_type('KCHR',unsafe);
  3592. add_type('LDEF',unsafe);
  3593. add_type('MBDF',unsafe);
  3594. add_type('MDEF',unsafe);
  3595. add_type('MMAP',unsafe);
  3596. add_type('SERD',unsafe);
  3597. add_type('WDEF',unsafe);
  3598. add_type('boot',unsafe);
  3599. add_type('insc',unsafe);
  3600. add_type('XCMD',unsafe);
  3601. add_type('XFNC',unsafe);
  3602. add_type('atpl',unsafe);{used by the "scores" virus}
  3603. add_type('DATA',unsafe);{used by the "scores" virus}
  3604. {RESOURCE TYPES KNOWN NOT TO CONTAIN EXECUTABLE CODE}
  3605. add_type('FONT',safe);
  3606. add_type('ALRT',safe);
  3607. add_type('BNSL',safe);
  3608. add_type('DITL',safe);
  3609. add_type('DLOG',safe);
  3610. add_type('FOND',safe);
  3611. add_type('FONT',safe);
  3612. add_type('ICN#',safe);
  3613. add_type('ICON',safe);
  3614. add_type('MENU',safe);
  3615. add_type('PAT ',safe);
  3616. add_type('PAT#',safe);
  3617. add_type('PICT',safe);
  3618. add_type('PREC',safe);
  3619. add_type('SIZE',safe);
  3620. add_type('STR ',safe);
  3621. add_type('STR#',safe);
  3622. add_type('TEXT',safe);
  3623. add_type('LAYO',safe);{desktop layout}
  3624. add_type('PAPA',safe);{chooser setting}
  3625. add_type('PREF',safe);{print monitor preferences}
  3626. add_type('CNTL',safe);
  3627. add_type('CURS',safe);
  3628. add_type('NFNT',safe);
  3629. add_type('fndr',safe);
  3630. add_type('itl0',safe);{date/time formats no code hooks}
  3631. add_type('scrn',safe);{control panel screen settings}
  3632. add_type('BMLS',safe);{appleshare server settings in Appleshare Prep}
  3633. add_type('clut',safe);
  3634. add_type('clst',safe);
  3635. add_type('mach',safe);
  3636. add_type('nrct',safe);
  3637.  
  3638. {add_type('',safe);}
  3639. end;
  3640. {$S event}
  3641. procedure setdefaultbutton(value:integer);
  3642. {set default button for pauses, questions and force updates}
  3643. var rr:rect;
  3644. begin
  3645. setport(mainwindow);
  3646. if defaultbutton<>0 then
  3647.     begin
  3648.       rr:=buttonrects[defaultbutton];
  3649.       insetrect(rr,-5,-5);
  3650.       Invalrect(rr);
  3651.     end;
  3652. defaultbutton:=value;
  3653. if defaultbutton<>0 then
  3654.     begin
  3655.       rr:=buttonrects[defaultbutton];
  3656.       insetrect(rr,-5,-5);
  3657.       Invalrect(rr);
  3658.     end;
  3659. end;
  3660.  
  3661. procedure wait_for_buttons{(ss:str255;default:integer)};
  3662. {Wait, display message, and give a chance to:
  3663.     "continue", "halt" or "shutdown"}
  3664. begin
  3665. setdefaultbutton(default);
  3666. askanswered:=false;
  3667. HiLiteControl(buttons[continuebut],0);{active}
  3668. PostStatus(ss,AskLine);
  3669. repeat 
  3670. doEvent(true);
  3671. until askanswered;
  3672. setdefaultbutton(nodefaultbut);
  3673. clear_to_end(askline);
  3674. HiLiteControl(buttons[continuebut],255);{inactive}
  3675. doevent(false);
  3676. end;
  3677. procedure wait_for_start(ss:str255;waitsecs:integer);
  3678. {wait, display message and startup buttons:
  3679.     "ShortCheck", "FullCheck", "SkipIt" "Shutdown"}
  3680.     {if a certian time has elapsed, continue}
  3681. var default:integer;
  3682.     wait,waituntil:longint;
  3683.     tag:str255;
  3684. begin
  3685. default:=startupdefaultbutton;;
  3686. wait:=waitsecs*60;
  3687. numtostring(waitsecs,tag);
  3688. tag:=concat(concat('  (Auto start after ',tag), ' sec)');
  3689. setdefaultbutton(default);
  3690. {askanswered:=false;}{set in initialize instead}
  3691. HiLiteControl(buttons[continuebut],0);{active}
  3692. {poststatus(tag,errorline);}
  3693. ss:=concat(ss,tag);
  3694. PostStatus(ss,AskLine);
  3695. doevent(false);
  3696. waituntil:=wait+tickcount;
  3697. repeat 
  3698. doEvent(true);
  3699. until ((askanswered) or (tickcount>waituntil));
  3700. setdefaultbutton(nodefaultbut);
  3701. clear_to_end(errorline);
  3702. FlushEvents(MDownMask,0);
  3703. HiLiteControl(buttons[continuebut],255);{inactive}
  3704. doevent(false);
  3705. end;
  3706.  
  3707. {$S startup}
  3708. procedure mySFold(      Var filevar     : text;
  3709.                         prompt          :str255;
  3710.                         var filepara    :str255;
  3711.                         var cancel:boolean);
  3712. {
  3713.   Do a Standard file open dialog to open an existing TEXT file 
  3714.   as a TURBO PASCAL text file.
  3715.  Use the toolbox to get the file name and set the default vol/folder.
  3716.  Use Reset to do the actual open for Turbo.
  3717.  This may work only on the 128k ROMS
  3718. }
  3719. var
  3720.    topleft,center    :point;
  3721.    ShowTypes  : SFTypeList;
  3722.    NTypes     :integer ;
  3723.    theErr     :OSErr;
  3724.    Reply      :SFreply;
  3725.    filename   :string[63];
  3726.    ScrHres,ScrVres : integer;
  3727.    vol        : integer;
  3728.    vserr       :OSerr;
  3729.   
  3730.    
  3731. begin
  3732.   filepara:='';
  3733.   with center do
  3734.   begin
  3735.   with screenbits.bounds do
  3736.     begin
  3737.       v:=(top+bottom) div 2;
  3738.       h:=(left+right) div 2;
  3739.     end;
  3740.   end;
  3741.  
  3742.   topleft.h:=center.h-170; {position of topleft}
  3743.   topleft.v:=center.v-120;
  3744.  
  3745.   ShowTypes[0]:='TEXT';
  3746.   Ntypes:=1;
  3747.   Cancel:=false;
  3748.   SFGetFile(topleft,prompt,nil,NTypes,ShowTypes,nil,Reply);
  3749.   if Reply.good then
  3750.       begin
  3751.  
  3752.        vol:=reply.vrefnum;
  3753.        filename:=reply.fname;
  3754.        vserr:=SetVol(nil,vol); {change default volume}
  3755.  
  3756.        {SFGetFile does not do an actual open : FSOpen or PBOpen
  3757.         are called to do this in the examples I have seen}
  3758.  
  3759.         reset(filevar,filename); {do turbo open}
  3760.         filepara:=filename;
  3761.       end
  3762.   else
  3763.       begin
  3764.       {may be a cancel or other error}
  3765.       Cancel:=true;
  3766.  
  3767.       end;
  3768.  
  3769. end; {of proc MySFold }
  3770.  
  3771. procedure mySFnew(      Var filevar : text;
  3772.                         prompt      :str255;
  3773.                         orgname     :str255;
  3774.                         Creator     :OStype;
  3775.                         var cancel:boolean);
  3776. {
  3777.   Do a Standard file put dialog to open a new TEXT file as a TURBO PASCAL
  3778.   text file for output.
  3779.   Use the toolbox to get the file name and set the default vol/folder.
  3780.   The use Rewrite to do the actual open for Turbo.
  3781.   This may work only on the 128k ROMS.
  3782. }
  3783. var
  3784.    topleft,center    :point;
  3785.    theErr     :OSErr;
  3786.    Reply      :SFreply;
  3787.    filename   :string[63];
  3788.    ScrHres,ScrVres : integer;
  3789.    vol        :integer;
  3790.    vserr       :OSerr;
  3791.    finderinfo  :finfo;
  3792.    
  3793. begin
  3794.   with center do
  3795.   begin
  3796.   with screenbits.bounds do
  3797.     begin
  3798.       v:=(top+bottom) div 2;
  3799.       h:=(left+right) div 2;
  3800.     end;
  3801.   end;
  3802.  
  3803.   topleft.h:=center.h-170; {position of topleft}
  3804.   topleft.v:=center.v-120;
  3805.  
  3806.   Cancel:=false;
  3807.   SFPutFile(topleft,prompt,orgname,nil,Reply);
  3808.   if Reply.good then
  3809.       begin
  3810.  
  3811.        vol:=reply.vrefnum;
  3812.        filename:=reply.fname;
  3813.  
  3814.        vserr:=SetVol(nil,vol); {change default volume}
  3815.  
  3816.        {SFGetFile does not do an actual open : FSOpen or PBOpen
  3817.         are called to do this in the examples I have seen}
  3818.  
  3819.         rewrite(filevar,filename); {do turbo open}
  3820.         
  3821.         {set file creator}
  3822.         if getFinfo(filename,vol,finderinfo)=NoErr then
  3823.             begin
  3824.                finderinfo.fdCreator:=creator;
  3825.                
  3826.                if setFinfo(filename,vol,finderinfo) <> NoErr then
  3827.                   begin
  3828.                     sysbeep(10);
  3829.                     cancel:=true;
  3830.                   end
  3831.                   ;
  3832.             end
  3833.         else
  3834.             begin
  3835.               sysbeep(10);
  3836.               cancel:=true;
  3837.             end
  3838.         
  3839.       end
  3840.   else
  3841.       begin
  3842.       {may be a cancel or other error}
  3843.       Cancel:=true;
  3844.  
  3845.       end;
  3846.  
  3847. end; {of proc MySFopen }
  3848. {$S startup}
  3849. procedure open_output;
  3850. {Open Output file}
  3851. var cancel:boolean;
  3852. begin
  3853. set_default_blessed;
  3854. {default folder to the system folder}
  3855. mySFnew(outfile,'Output File?','NewSystemCheckSum','EDIT',cancel);
  3856. if not cancel then
  3857.     begin
  3858.     outputopen:=true;
  3859.     writeoutputflag:=true;
  3860.     end
  3861.  else
  3862.     begin
  3863.     outputopen:=false;
  3864.     writeoutputflag:=false;
  3865.     end;
  3866. set_default_blessed;    
  3867. end;
  3868. {$S event}
  3869. procedure open_input;
  3870. {look for input file 'OldSystemCheckSum' in startup and blessed folder
  3871. if not found ask for it}
  3872. label 99;
  3873. var savewd:integer;
  3874.     err,ierr,ignore:oserr;
  3875.     name:str255;
  3876.     finder:Finfo;
  3877. begin
  3878. name:='OldSystemCheckSum';
  3879. inputnotdefault:=true;
  3880.  
  3881. if inputopen then exit;
  3882. ignore:=getvol(nil,savewd);
  3883. {startup folder}
  3884. ignore:=setvol(nil,startupwd);
  3885. err:=GetFInfo(name,0,finder);
  3886. if err=noerr then
  3887.     begin
  3888.       if finder.fdtype='TEXT' THEN
  3889.           begin
  3890.           reset(infile,name);
  3891.           ierr:=IOResult;{turbo pascal error codes}
  3892.           if Ierr<>0 then halt_on_error(ierr,'Open Input-default folder');
  3893.           inputopen:=true;
  3894.           inputnotdefault:=false;
  3895.           goto 99;
  3896.           end;
  3897.     end;
  3898. {system folder}
  3899. set_default_blessed;
  3900. err:=GetFInfo(name,0,finder);
  3901. if err=noerr then
  3902.     begin
  3903.       if finder.fdtype='TEXT' THEN
  3904.           begin
  3905.           reset(infile,name);
  3906.           ierr:=IOResult;{turbo pascal error codes}
  3907.           if Ierr<>0 then halt_on_error(ierr,'Open Input-system folder');
  3908.           inputopen:=true;
  3909.           inputnotdefault:=false;
  3910.           goto 99;
  3911.           end;
  3912.     end;
  3913. poststatus(concat('I can''t find: ',name),errorline);
  3914. if Ask('Do you want to specify another input file?',nodefaultbut) then
  3915.      begin
  3916.        poststatus('',errorline);
  3917.        mySFold(infile,'Old checksums file',name,cancel);
  3918.        if cancel then goto 99;
  3919.        inputopen:=true;
  3920.        goto 99;
  3921.      end
  3922.      else
  3923.        begin
  3924.          poststatus('',errorline);
  3925.        end;
  3926. 99:
  3927. ignore:=setvol(nil,savewd);
  3928. end;{open_input}
  3929.  
  3930. procedure close_and_flush(var filevar:text;var openflag:boolean);
  3931. {close file and flush default volume}
  3932. var ignore:oserr;
  3933. begin
  3934. if openflag then close(filevar);
  3935. openflag:=false;
  3936. ignore:=FlushVol(nil,0);{flush default volume}
  3937. end;{procedure close_and_flush}
  3938.  
  3939. procedure close_all_and_halt{(beep:boolean)};
  3940. {Close input and output files if open and halt}
  3941. var ignore:oserr;
  3942. begin
  3943. if beep then 
  3944.    begin sysbeep(1);sysbeep(1); end;
  3945. if inputopen then close_and_flush(infile,inputopen);
  3946. if outputopen then close_and_flush(outfile,outputopen);
  3947. closepath(myRpath); 
  3948. ignore:=setvol(nil,startupwd);
  3949. halt;
  3950. end;
  3951. {$S boot}
  3952. function absolute_read(buffer:handle;count:longint;offset:longint):oserr;
  3953. {read data from an absolute position-used to check the boot blocks}
  3954. var pblock:paramBlockRec;
  3955.     mypb:hparamblockrec;
  3956.     vname:str255;
  3957.     err:oserr;
  3958.     drivenum:integer;
  3959.     driver:integer;    
  3960. begin
  3961. {get drive number of default volume}
  3962. with mypb do
  3963.     begin
  3964.     iocompletion:=nil;
  3965.     vname:='';
  3966.     ionameptr:=@vname;
  3967.     iovrefnum:=0;
  3968.     ioVolIndex:=0;
  3969.     end;
  3970. scsi_wait;
  3971. err:=pbhgetvinfo(@mypb,false);
  3972. with mypb do
  3973.     begin
  3974.       drivenum:=iovdrvinfo;
  3975.       driver:=iovdrefnum;
  3976.      end;
  3977.  
  3978. absolute_read:=err;
  3979.  
  3980. if err<>noerr then 
  3981.    begin
  3982.       exit;
  3983.    end;
  3984.    
  3985. {work on reading the data}
  3986. hlock(buffer);
  3987. with pblock do
  3988.      begin
  3989.         iocompletion:=nil;
  3990.         iovrefnum:=drivenum;
  3991.         iorefnum:=driver;
  3992.         iobuffer:=buffer^;
  3993.         ioreqcount:=count;
  3994.         ioPosMode:=FsFromStart;{relative from first sector}
  3995.         ioPosoffset:=offset;         
  3996.      end;
  3997.     scsi_wait;
  3998.     err:=PBRead(@pblock,false);
  3999. hunlock(buffer);
  4000. absolute_read:=err;
  4001. end;{proc absolute_read}
  4002.  
  4003. {$S boot}
  4004. procedure boot_ignore(buffer:handle);
  4005. {zero out fields where boot block changes are safe/common}
  4006. type boots=array[0..511] of integer;
  4007.      bootptr=^boots;
  4008. var i:integer;
  4009.     p:bootptr;
  4010. begin
  4011. hlock(buffer);
  4012. p:=bootptr(buffer^);
  4013. {info that changes on set startup}
  4014. for i:=45 to 52 do p^[i]:=0;
  4015. hunlock(buffer);
  4016. end;
  4017.  
  4018. function checksum_boot_blocks{:integer};
  4019. var count,offset:longint;
  4020.     buffer:handle;
  4021.     err:oserr;
  4022.     result:integer;
  4023. begin
  4024. result:=0;
  4025. count:=1024;{two logical blocks}
  4026. offset:=0;
  4027. buffer:=newhandle(count);
  4028. if buffer<>nil then
  4029.    begin
  4030.       err:=absolute_read(buffer,count,offset);
  4031.       if err=noerr then 
  4032.            begin
  4033.            boot_ignore(buffer);{zero out safe stuff}
  4034.            result:=checksumHdata(buffer);
  4035.            end;
  4036.       disposHandle(buffer);
  4037.    end;
  4038.  
  4039. checksum_boot_blocks:=result;
  4040.  
  4041. end;{proc}
  4042.  
  4043. {$S event}
  4044. procedure tefixup(statustext:tehandle);
  4045. {shrink the size of the TERec if a bug in TE has made the
  4046. linestarts array too big}
  4047. var  base,needed,actual:longint;
  4048. begin
  4049. actual:=gethandlesize(handle(statustext));
  4050. {figure nominal size}
  4051. base:=sizeof(statustext^^)-sizeof(statustext^^.linestarts);
  4052. needed:=(statustext^^.nlines+1)*2+base;
  4053. if actual>needed+64 then
  4054.     begin
  4055.     {reset to needed size plus a bit extra}
  4056.        Hunlock(handle(statustext));
  4057.        sethandlesize(handle(statustext),needed+8);
  4058.     end
  4059. end;{procedure tefixup}
  4060.  
  4061. procedure replaceline{(ss:str255;linenum:integer)};
  4062. {On screen messages}
  4063. {replace a line in the statustext TERec}
  4064. var start,finish:longint;
  4065.     cr:string[1];
  4066. begin
  4067. cr:=chr(13);
  4068. ss:=concat(ss,cr);
  4069. if linenum<statustext^^.nlines then
  4070.    begin
  4071.    start:=statustext^^.linestarts[linenum-1];
  4072.    finish:=statustext^^.linestarts[linenum];
  4073.    end
  4074. else
  4075.    begin
  4076.    start:=0;
  4077.    finish:=0;
  4078.    end;
  4079. TESetSelect(start,finish,statustext);
  4080. TeDeLete(statustext);
  4081. TEInsert(Pointer(ord4(@ss)+1),length(ss),statustext);
  4082. tefixup(statustext);
  4083. TESetSelect(0,0,statustext);
  4084. end;
  4085.  
  4086. procedure clear_to_end{(linenum:integer)};
  4087. {On screen messages}
  4088. {clear lines in the statustext TERec from linenum to end}
  4089. var start,finish:longint;
  4090.     nn:integer;
  4091.     cr:string[1];
  4092.     ss:str255;
  4093. begin
  4094. cr:=chr(13);
  4095. if linenum<statustext^^.nlines then
  4096.    begin
  4097.    start:=statustext^^.linestarts[linenum-1];
  4098.    finish:=statustext^^.telength;
  4099.    end
  4100. else
  4101.    begin
  4102.    start:=0;
  4103.    finish:=0;
  4104.    end;
  4105. {create empty lines as filler}
  4106. ss:=cr;
  4107. for nn:=linenum to mstatus do ss:=concat(cr,ss);
  4108.  
  4109. TESetSelect(start,finish,statustext);
  4110. TeDeLete(statustext);
  4111. TEInsert(Pointer(ord4(@ss)+1),length(ss),statustext);
  4112. TESetSelect(0,0,statustext);
  4113. {replace title and byline}
  4114. if linenum<=byline then
  4115.   begin
  4116. Replaceline(concat('Startup System Check ',TitleVersion),titleline);
  4117.  ReplaceLine('by Albert Lunde, Northwestern University  Copyright © 1988'
  4118. ,byline);
  4119.   end;
  4120.  
  4121. end;
  4122.  
  4123. procedure postmem(linenum:integer);
  4124. var ff,kk:str255;
  4125. begin
  4126. if not showdebuginfo then exit;
  4127. numtostring(freemem,ff);
  4128. kk:=concat('Free memory: ',ff);
  4129. poststatus(kk,linenum);
  4130. end;
  4131. procedure poststatus{(ss:str255;linenum:integer)};
  4132. {On screen messages}
  4133. {post a message on the screen and go into the event
  4134. loop long enought to update the screen or process halt button} 
  4135. var ff,kk:str255;
  4136. begin
  4137. (*
  4138. numtostring(freemem,ff);
  4139. kk:=ff;
  4140. numtostring(gethandlesize(handle(statustext)),ff);
  4141. kk:=concat(kk,concat(' ',ff));
  4142. numtostring(sizeof(resourceinfoarray),ff);
  4143. kk:=concat(kk,concat(' ',ff));
  4144. *)
  4145. replaceline(ss,linenum);
  4146. {replaceline(kk,mstatus);}
  4147. DoEvent(false);
  4148. end; 
  4149.  
  4150. function Ask{(question:str255;default:integer):boolean};
  4151.  
  4152. begin
  4153. setdefaultbutton(default);
  4154. hidecontrol(buttons[continuebut]);
  4155. hidecontrol(buttons[shutdownbut]);
  4156. hidecontrol(buttons[haltbut]);
  4157. showcontrol(buttons[yesbut]);
  4158. showcontrol(buttons[nobut]);
  4159. askanswered:=false;
  4160. poststatus(question,askline);
  4161. sysbeep(1);
  4162. repeat
  4163. doevent(false);
  4164. until askanswered;
  4165. setdefaultbutton(nodefaultbut);
  4166. clear_to_end(askline);
  4167. showcontrol(buttons[continuebut]);
  4168. showcontrol(buttons[shutdownbut]);
  4169. showcontrol(buttons[haltbut]);
  4170. hidecontrol(buttons[yesbut]);
  4171. hidecontrol(buttons[nobut]);
  4172. ask:=askanswer;
  4173. doevent(true);
  4174. end;
  4175.  
  4176. procedure halt_on_error{(err:oserr;sss:str255)};
  4177. {check for OSerr code}
  4178. var ss:str255;
  4179. begin
  4180. if err=noerr then exit;
  4181. Numtostring(err,ss);
  4182. ss:=concat(concat('Unexpected Error:',ss),sss);
  4183. poststatus(ss,errorline);
  4184. repeat until button;
  4185. close_all_and_halt(true);
  4186. end;
  4187. {$S files}
  4188. procedure folder_info(wdrefnum:integer;
  4189.                 var dirid:longint;
  4190.                 var volume:integer;
  4191.                 var name:str255;
  4192.                 var path:str255);
  4193. {get info (name and path) on a directory specified by either a
  4194. 16 bit working directory reference number or
  4195. a 32 bit id directory ID}
  4196. {set wdrefnum zero on input if using a dir id}
  4197. {this only seems to work on the current volume}
  4198. var vname:str255;
  4199.     mywdpb:wdpbrec;
  4200.     mycinfopb:cinfopbrec;
  4201.     err,ignore:oserr;
  4202.     oldwd:integer;
  4203.     fail:boolean;
  4204.     tempdirid:longint;
  4205.     tempname:str255;
  4206.     count:integer;
  4207. begin
  4208. fail:=false;
  4209. {save default wd}
  4210. ignore:=getvol(nil,oldwd);
  4211.  
  4212.  
  4213.  
  4214. if not fail then
  4215. with mywdpb do
  4216.     begin
  4217.         {set up for PBHSet/GetVol call}
  4218.          ioCompletion:= NIL;    
  4219.          vname:='';
  4220.          ioNamePtr:= @vName;    {initialize may not be needed}
  4221.          ionameptr:=nil;
  4222.          iowddirid:=dirid;
  4223.          iovrefnum:=wdrefnum;
  4224.     end;
  4225. scsi_wait;
  4226. {do a set then get to make sure we always know the dir id afterwards}    
  4227. err:=PBHSetVol(@mywdpb,false);
  4228. fail:=fail or (err<>noerr);
  4229. err:=PBHGetVol(@mywdpb,false);
  4230. fail:=fail or (err<>noerr);
  4231. if not fail then
  4232.    begin
  4233.         with mywdpb do
  4234.             begin
  4235.                 {return from PBHGETVol call}
  4236.                 dirid:=ioWDDIRID;
  4237.                 volume:=ioWDVRefNum;
  4238.             end;{with}
  4239.    end;  
  4240.  
  4241.  
  4242.  
  4243.  
  4244. {now DirID is the 32-bit id of a directory}
  4245. if not fail then
  4246.     begin       
  4247.         {Build path}
  4248.         {This is based roughly on C code from
  4249.         "Programming with Macintosh Programmer's Workshop" by Joel West
  4250.         Page 467-469}
  4251.  
  4252.         path:='';
  4253.         tempdirid:=dirid;{dir ID for folders along the path}
  4254.         tempname:='';
  4255.         count:=0;{infinite loop protection}
  4256.         repeat 
  4257.           count:=count+1;
  4258.           with mycinfopb do
  4259.                begin
  4260.                {setup for pbgetcatinfo}
  4261.                iocompletion:=nil;
  4262.                ionameptr:=@tempname;
  4263.                iovrefnum:=0;
  4264.                iodirid:=tempdirid;
  4265.                iofdirindex:=-1;{info about directories only see tn#69}
  4266.                end;
  4267.                scsi_wait;
  4268.                err:=pbgetcatinfo(@mycinfopb,false);
  4269.                   if err=noerr then
  4270.                     with mycinfopb do
  4271.                         begin
  4272.                         if count=1 then name:=tempname;
  4273.                         path:=concat(tempname,':',path);
  4274.                         tempdirid:=iodrparid;
  4275.                         end;
  4276.  
  4277.         until((count>100) or (tempdirid=fsrtparid) )
  4278.     end;{path build} 
  4279. {restore default wd}
  4280. ignore:=setvol(nil,oldwd);  
  4281. end;{folderinfo}
  4282.  
  4283. procedure folder_name{(dirid:longint;var name:str255)};
  4284. {get name of a directory specified by a
  4285.  32 bit id directory ID}
  4286.  
  4287. var vname:str255;
  4288.     mywdpb:wdpbrec;
  4289.     mycinfopb:cinfopbrec;
  4290.     err,ignore:oserr;
  4291.     oldwd:integer;
  4292.     tempname:str255;
  4293. begin
  4294. {save default wd}
  4295. ignore:=getvol(nil,oldwd);
  4296.  
  4297. with mywdpb do
  4298.     begin
  4299.         {set up for PBHSet/GetVol call}
  4300.          ioCompletion:= NIL;    
  4301.          vname:='';
  4302.          ioNamePtr:= @vName;    {initialize may not be needed}
  4303.          ionameptr:=nil;
  4304.          iowddirid:=dirid;
  4305.          iovrefnum:=0;
  4306.     end;
  4307.  
  4308. scsi_wait;
  4309. err:=PBHSetVol(@mywdpb,false);
  4310. name:='';
  4311. if err=noerr then
  4312.     begin       
  4313.       with mycinfopb do
  4314.            begin
  4315.            {setup for pbgetcatinfo}
  4316.            iocompletion:=nil;
  4317.            ionameptr:=@tempname;
  4318.            iovrefnum:=0;
  4319.            iodirid:=dirid;
  4320.            iofdirindex:=-1;{info about directories only see tn#69}
  4321.            end;{with}
  4322.    scsi_wait;
  4323.       err:=pbgetcatinfo(@mycinfopb,false);
  4324.          if err=noerr then
  4325.            with mycinfopb do
  4326.                begin
  4327.                name:=tempname;
  4328.                end;
  4329.     end;{path build} 
  4330. {restore default wd}
  4331. ignore:=setvol(nil,oldwd);  
  4332. end;{folderinfo}
  4333.  
  4334. procedure set_default_blessed;
  4335. {set default volume and folder to the blessed(active system) folder}
  4336. var
  4337.          err: OSErr;  
  4338.          myWDPB: WDPBRec;
  4339.       dummy:str255;
  4340. begin
  4341. {set default folder to dirID}
  4342. with mywdpb do
  4343.     begin
  4344.         {set up for PBHSetVol call}
  4345.          ioCompletion:= NIL;    
  4346.          dummy:='';
  4347.          ioNamePtr:= @dummy;    {initialize may not be needed}
  4348.          ionameptr:=nil;
  4349.          ioWDDirID:=blessed;
  4350.          ioVRefNum:=blessedbootvolwd;
  4351.     end;
  4352. scsi_wait;
  4353. err:=PBHSetVol(@mywdpb,false);
  4354.     
  4355.  halt_on_error(err,'PBsetvol-set_default_blessed');
  4356.  
  4357.  
  4358. end;{procedure}
  4359.  
  4360. procedure set_default_by_id{(DirID:longint)};
  4361. {set default folder by 32 bit DirId}
  4362. var
  4363.          err: OSErr;  
  4364.          myWDPB: WDPBRec;
  4365.       dummy:str255;
  4366. begin
  4367. {set default folder to dirID}
  4368. with mywdpb do
  4369.     begin
  4370.         {set up for PBHSetVol call}
  4371.          ioCompletion:= NIL;    
  4372.          dummy:='';
  4373.          ioNamePtr:= @dummy;    {initialize may not be needed}
  4374.          ionameptr:=nil;
  4375.          ioWDDirID:=dirID;
  4376.          ioVRefNum:=0;
  4377.     end;
  4378. scsi_wait;
  4379. err:=PBHSetVol(@mywdpb,false);
  4380.     
  4381.  halt_on_error(err,'PBsetvol-set_default_by_id');
  4382.  
  4383.  
  4384. end;{procedure}
  4385.  
  4386. PROCEDURE EnumerateCatalog(dirIDToSearch: longint);
  4387.  
  4388. {process all files in a folder but ignore subfolders}
  4389.  
  4390. VAR
  4391.  
  4392.       myCPB: CInfoPBRec;
  4393.  
  4394.       err: OSErr;  
  4395.  
  4396.       myWDPB: WDPBRec;
  4397.  
  4398.       TotalFiles,TotalDirectories: LONGINT;
  4399.    fname,dummy:str255;
  4400.  
  4401.   index:    integer;    
  4402.  
  4403.     ignore:oserr;
  4404.     oldwd:integer;    
  4405.  
  4406. Begin {EnumerateCatalog}
  4407. ignore:=getvol(nil,oldwd);{save old wd}
  4408.  
  4409.  
  4410.  TotalFiles:= 0;
  4411.  
  4412.     TotalDirectories:= 0;
  4413.  
  4414.     err:= PBHGetVol(@myWDPB,FALSE);        {get the default volume}     
  4415.  
  4416.     with MyCPB do Begin
  4417.  
  4418.         iocompletion:= Nil;
  4419.  
  4420.         ioNamePtr:= @FName;
  4421.  
  4422.         ioVRefNum:= myWDPB.ioVRefNum;      {for now, default vol, set this to what you want}
  4423.  
  4424.     End;  {with}
  4425.  
  4426. {set default folder to diridtosearch to allow 
  4427. use of high level calls in called procedures}
  4428.  
  4429.  set_default_by_id(diridtosearch);
  4430.  
  4431. index:= 1;
  4432.  
  4433.     repeat{loop over folder with index}
  4434.  doEvent(false);
  4435.         FName:= '';  {nil out name}
  4436.  
  4437.         myCPB.ioFDirIndex:= index;
  4438.  
  4439.         myCPB.ioDrDirID:= dirIDToSearch; {we need to do this every time through}
  4440.  
  4441.  
  4442.         err:= PBGetCatInfo(@myCPB,FALSE);
  4443.  
  4444.  
  4445.  
  4446.         If err = noErr then 
  4447.  
  4448.             if BitTst(@myCPB.ioFlAttrib,3) then 
  4449.       Begin {we have a dir}
  4450.                  TotalDirectories:=TotalDirectories+1;
  4451.         {do nothing for directories}
  4452.                  err:= 0;  {clear error return on way back}
  4453.              End {if BitTst}
  4454.         Else 
  4455.       Begin{we have a file}
  4456.  
  4457.             TotalFiles:= TotalFiles + 1;
  4458.  
  4459.     Poststatus(concat('Checking: ',fname),fileline);
  4460.     (* Do_for_file(dirIDToSearch,MyCPB.ioFrefNum,Fname,Totalfiles,MYCPB)*)
  4461.      sysfiles[index]:=fname;
  4462.     check_a_file(index);
  4463.         End; {else} 
  4464.   PostStatus('',fileline);
  4465.         index:= index + 1;
  4466.  
  4467.     until err <> noErr;
  4468.  
  4469. ignore:=setvol(nil,oldwd); {restore WD} 
  4470.  
  4471. End;  {EnumerateCatalog}
  4472.  
  4473. procedure get_blessed;
  4474. {get the blessed folder 32 bit dir id,
  4475. given either volume ref or a working directory ref on that volume}
  4476. {this version stores the blessed folder id and the working dir ref
  4477. of the boot volume in global variables}
  4478. CONST
  4479.  
  4480.     FSFCBLen    = $3F6;      {location of low-memory global FSFCBLen}
  4481.         
  4482.  
  4483. VAR
  4484.  
  4485.     myHPB: HParamBlockRec;    {for the PBHGetVInfo call}
  4486.  
  4487.     myWDPB: WDPBRec;        {for the PBHSetVol call}
  4488.  
  4489.     err,ignore: OSErr;  
  4490.  
  4491.     oldVol: integer;
  4492.  
  4493.     vName,fName: str255;
  4494.  
  4495.     HFS: ^integer;        {to check to see if we are running HFS}
  4496.  bootwdptr: ^integer;    {to find the boot drive}
  4497.  oldwd:integer;
  4498.  
  4499. begin
  4500.   {save default wd}
  4501.   ignore:=getvol(nil,oldwd);
  4502.  
  4503.        HFS:= POINTER(FSFCBLen);    {point our variable at the low-memory global}
  4504.  
  4505.     if HFS^ > 0 then Begin     {we're running HFS}
  4506.   
  4507.   blessedbootvolwd:=GetRealBootDrive;  
  4508.   {"working directory reference number" for system startup volume}
  4509.   
  4510.   {change to system startup volume - this is so we always find the
  4511.    blessed folder on the startup device, even when running from a floppy}
  4512.  
  4513.   ignore:=setvol(nil ,blessedbootvolwd);
  4514.   
  4515.         vName:= '';        {initialize this}
  4516.  
  4517.         with myHPB do Begin
  4518.  
  4519.             ioCompletion:= NIL;    
  4520.  
  4521.             ioNamePtr:= @vName;    {initialize}
  4522.  
  4523.             ioVRefNum:= 0;     {0 is get for default volume}
  4524.  
  4525.             ioVolIndex:= 0;    {we're not making indexed calls}
  4526.    
  4527.  
  4528.         End;  {with}
  4529.  
  4530.         
  4531.  
  4532.         err:= PBHGetVInfo(@myHPB,FALSE);
  4533.  
  4534.         if err <> 0 then poststatus('PBHGetVInfo Error',errorline)
  4535.  
  4536.         Else
  4537.  
  4538.  
  4539.     End {if HFS^ > 0}
  4540. ;
  4541. {At this point, the dirID of the blessed folder on the volume}
  4542. blessed:=myHPB.ioVFndrInfo[1];
  4543. {writeln(vname);}
  4544. {restore default wd}
  4545. ignore:=setvol(nil,oldwd);  
  4546.  
  4547.  
  4548. end;{get blessed}
  4549.  
  4550. {$S core}
  4551. procedure filltype{(var tt:restype;ss:str255)};
  4552. {blank fill and convert string to resource type}
  4553. var work:string[8];
  4554. begin
  4555.  work:=concat(ss,'    ');
  4556. tt[1]:=work[1];
  4557. tt[2]:=work[2];
  4558. tt[3]:=work[3];
  4559. tt[4]:=work[4];
  4560. end;{fill type}
  4561.  
  4562. {$S startup}
  4563. procedure read_input_header;
  4564. {read beginning of input file before resource types list}
  4565. var ignore:str255;
  4566. begin
  4567. if not inputopen then exit;
  4568. {Mac SE:System Folder: path,date,time,version 
  4569. 12742                  bootblockchecksum
  4570. 789087                 checksumchecksum
  4571. 322 104 322            resource counts
  4572. *****                  end of header}
  4573. if eof(infile) then exit;
  4574. read_input(ignore);{path}
  4575. if eof(infile) then exit;
  4576. oldbootblockchecksum:=0;
  4577. read_input_integer(oldbootblockchecksum);{grand resource checksum}
  4578.  
  4579. if eof(infile) then exit;
  4580. oldchecksumchecksum:=0;
  4581. read_input_long(oldchecksumchecksum);  {boot block checksum}
  4582.  
  4583. {skip down to *****}
  4584. repeat
  4585. if eof(infile) then exit;
  4586. read_input(ignore);
  4587. until(test_end_flag(ignore));
  4588.  
  4589. read_safekeys;
  4590. read_morechecks;
  4591. end;{proc}
  4592.  
  4593. procedure readoklist;
  4594. {read a list of resource types/stop at eof or "*****"}
  4595. {each line contains TYPE,safety level,and optional number of occurances}
  4596. label 99;
  4597. var line:str255;
  4598.     ntokens:integer;
  4599.     tokens:tokenstype;
  4600.     atype:restype;
  4601.     howsafe:safetype;
  4602.     work:longint;
  4603. begin
  4604. if not inputopen then exit;
  4605. PostStatus('Reading safe types list',fileline);
  4606. while(not eof(infile)) do
  4607.    begin
  4608.       line:='';
  4609.       read_input(line);
  4610.       {sysbeep(1);
  4611.       poststatus(line,fileline);}
  4612.       if test_end_flag(line) then goto 99;
  4613.       tabscan(line,tokens,ntokens);
  4614.      { poststatus(concat(concat('$',tokens[1]),'$'),errorline);}
  4615.       if ntokens>=2 then
  4616.           begin 
  4617.             filltype(atype,tokens[1]);
  4618.             work:=ord(unknown);
  4619.             stringtonum(tokens[2],work);
  4620.             if (work>3) or (work<0) then
  4621.                  begin
  4622.                  poststatus(concat('Bad input:',line),errorline);
  4623.                  wait_for_buttons(' ',continuebut);
  4624.                  howsafe:=unknown;
  4625.                  end
  4626.             else
  4627.                  howsafe:=safetype(work);
  4628.             add_type(atype,howsafe);
  4629.           end
  4630.       else if ntokens=1 then
  4631.           begin 
  4632.             filltype(atype,tokens[1]);
  4633.             add_type(atype,safe);
  4634.           end;
  4635.                    
  4636.    end;{while}
  4637. 99:
  4638. PostStatus('',fileline);
  4639. end;{readoklist}
  4640.  
  4641. {$S sortres}  
  4642.         procedure sortresources(var X:resourceinfoarrayptr;N:integer);
  4643.         {sort array of resources and their checksums}
  4644. {        HEAP SORT
  4645. C
  4646. C       BASED ON PROGRAMMING PEARLS BY JON BENTLEY P 182
  4647. C       X IS THE ARRAY CONTAINING NX ELEMENTS TO BE SORTED
  4648. C       CALL SWAPX(I,J) SWAPS X(I) WITH X(J)
  4649. C       GEX(X,I,J) IS TRUE IFF X(I) >= X(J)
  4650. C       GTX(X,I,J) IS TRUE IFF X(I) > X(J)
  4651. C}
  4652.  
  4653.  
  4654.        var i: integer;
  4655.        
  4656.        
  4657. procedure SWAPX(I:integer;J:integer);
  4658. var     T:resourceinforec;
  4659.         
  4660. begin
  4661.         T:=X^[I];
  4662.         X^[I]:=X^[J];
  4663.         X^[J]:=T;
  4664. END; {of procedure swapx}
  4665.  
  4666. FUNCTION GTX(I:integer;J:integer):boolean;
  4667. var filecomp:integer;
  4668. begin
  4669.        {sort type,id,size }
  4670.        gtx:=false;
  4671.        
  4672.         filecomp:=filenamecompare(
  4673.             sysfiles[(X^[I].filenameindex and fnamemask)],
  4674.             sysfiles[(X^[J].filenameindex and fnamemask)]);
  4675.             
  4676.         if filecomp>0
  4677.           {(sysfiles[(X^[I].filenameindex and fnamemask)]>
  4678.             sysfiles[(X^[J].filenameindex and fnamemask)])} then
  4679.            begin
  4680.             gtx:=true
  4681.            end
  4682.         else if filecomp=0
  4683.                {((X^[I].filenameindex and fnamemask)=
  4684.                  (X^[J].filenameindex and fnamemask))} then
  4685.              if (X^[I].thetype>X^[J].thetype) then
  4686.                 begin
  4687.                  gtx:=true
  4688.                 end
  4689.              else if (X^[I].thetype=X^[J].thetype) then
  4690.                 begin
  4691.                   if (X^[I].theid>X^[J].theid)then
  4692.                       begin
  4693.                           gtx:=true; 
  4694.                       end
  4695.                   else if (X^[I].theid=X^[J].theid)then
  4696.                       begin
  4697.                         if (X^[I].thesize>X^[J].thesize)then
  4698.                             begin
  4699.                               gtx:=true;
  4700.                             end
  4701.                       end
  4702.                    ;
  4703.                  end
  4704.              ;
  4705.                    
  4706. end;
  4707.         
  4708. FUNCTION GEX(I:integer;J:integer):boolean;
  4709. var filecomp:integer;
  4710. begin
  4711.        {sort type,id,size }
  4712.        gex:=false;
  4713.        
  4714.         filecomp:=filenamecompare(
  4715.             sysfiles[(X^[I].filenameindex and fnamemask)],
  4716.             sysfiles[(X^[J].filenameindex and fnamemask)]);
  4717.             
  4718.        if filecomp>0
  4719.          {(sysfiles[(X^[I].filenameindex and fnamemask)]>
  4720.            sysfiles[(X^[J].filenameindex and fnamemask)])} then
  4721.            begin
  4722.             gex:=true
  4723.            end
  4724.         else if filecomp=0
  4725.                {((X^[I].filenameindex and fnamemask)=
  4726.                  (X^[J].filenameindex and fnamemask))} then             
  4727.            if (X^[I].thetype>X^[J].thetype) then
  4728.               begin
  4729.                gex:=true
  4730.               end
  4731.            else if (X^[I].thetype=X^[J].thetype) then
  4732.               begin
  4733.                 if (X^[I].theid>X^[J].theid)then
  4734.                     begin
  4735.                         gex:=true;
  4736.                     end
  4737.                 else if (X^[I].theid=X^[J].theid)then
  4738.                     begin
  4739.                      if (X^[I].thesize>=X^[J].thesize)then
  4740.                          begin
  4741.                            gex:=true;
  4742.                          end
  4743.                     end
  4744.                  ;
  4745.                end
  4746.            ;
  4747.  
  4748. END;
  4749.        
  4750. procedure siftdown(L:integer;U:integer);
  4751.     label 300,999{return};
  4752.     var
  4753.         i,child:integer;
  4754.         
  4755. begin
  4756.         
  4757. {
  4758. C
  4759. C       BEFORE MAXHEAP(L+1,U)
  4760. C       AFTER  MAXHEAP(L,U)
  4761. }
  4762.         I:=L;
  4763.         
  4764.         {LOOP}
  4765. 300:
  4766. {
  4767. C               INVARIANT: MAXHEAP(L,U) EXCEPT PERHAPS
  4768. C                       BETWEEN I AND ITS (0,1 OR 2) CHILDREN
  4769. C
  4770. }
  4771.                 CHILD:=2*I;
  4772.  
  4773.                 IF CHILD > U  then goto 999;
  4774. {
  4775. C
  4776. C               IF C+1 <= U AND X^(C+1) > X^(C) THEN C=C+1
  4777. C
  4778. }
  4779.                 IF(CHILD+1 <= U) THEN
  4780.                 IF(GTX(CHILD+1,CHILD))THEN
  4781.                         CHILD:=CHILD+1;
  4782.  
  4783. {                
  4784. C
  4785. C               CHILD IS THE GREATEST CHILD OF I
  4786. C
  4787. C               IF X^(I) >= X^(CHILD) THEN RETURN
  4788. C
  4789. }
  4790.                 IF(GEX(I,CHILD)) then goto 999;
  4791.                 
  4792. {                
  4793. C
  4794. C               X^(I) IS LESS THAN ITS GREATEST CHILD SO SWAP IT DOWNWARD
  4795. C               AND REPEAT LOOP
  4796. C
  4797. }
  4798.                 SWAPX(CHILD,I);
  4799.                 I:=CHILD;
  4800.                 GOTO 300;
  4801.         {END LOOP}
  4802. 999:{return}
  4803. END; {of proc siftdown}
  4804.  
  4805.  
  4806.        
  4807. begin {main body of sortresources}
  4808.  
  4809.         for I:=N div 2 downto 1 do
  4810.         begin
  4811.        { echo(i);}
  4812.         SIFTDOWN(I,N);
  4813.         end;
  4814.  
  4815.         {echo(0);}
  4816.  
  4817.         for I:=N downto 2 do
  4818.         begin
  4819.           {  echo(i);}
  4820.                 SWAPX(1,I);
  4821.                 {echo(i);}
  4822.                 SIFTDOWN(1,I-1);
  4823.                { echo(i);}
  4824.          end;
  4825.  
  4826.  
  4827.  
  4828. END; {sortresources}
  4829.  
  4830.         procedure sorttypes{(var X:resourcetypeinfoarray;N:integer)};
  4831.         {sort resource types array}
  4832. {        HEAP SORT
  4833. C
  4834. C       BASED ON PROGRAMMING PEARLS BY JON BENTLEY P 182
  4835. C       X IS THE ARRAY CONTAINING NX ELEMENTS TO BE SORTED
  4836. C       CALL SWAPX(I,J) SWAPS X(I) WITH X(J)
  4837. C       GEX(X,I,J) IS TRUE IFF X(I) >= X(J)
  4838. C       GTX(X,I,J) IS TRUE IFF X(I) > X(J)
  4839. C}
  4840.  
  4841.  
  4842.        var i: integer;
  4843.        
  4844.        
  4845. procedure SWAPX(I:integer;J:integer);
  4846. var     T:resourcetypeinforec; 
  4847.         
  4848. begin
  4849.         T:=X[I];
  4850.         X[I]:=X[J];
  4851.         X[J]:=T;
  4852. END; {of procedure swapx}
  4853.  
  4854. FUNCTION GTX(I:integer;J:integer):boolean;
  4855.  
  4856. begin
  4857.        {sort type,id,size }
  4858.        gtx:=false;
  4859.             if (X[I].thetype>X[J].thetype) then
  4860.                 begin
  4861.                         gtx:=true;
  4862.                  end
  4863.              ;
  4864.                    
  4865. end;
  4866.         
  4867. FUNCTION GEX(I:integer;J:integer):boolean;
  4868.  
  4869. begin
  4870.        {sort type,id,size }
  4871.        gex:=false;
  4872.            if (X[I].thetype>=X[J].thetype) then
  4873.               begin
  4874.                gex:=true
  4875.               end
  4876.  
  4877. END;
  4878.        
  4879. procedure siftdown(L:integer;U:integer);
  4880.     label 300,999{return};
  4881.     var
  4882.         i,child:integer;
  4883.         
  4884. begin
  4885.         
  4886. {
  4887. C
  4888. C       BEFORE MAXHEAP(L+1,U)
  4889. C       AFTER  MAXHEAP(L,U)
  4890. }
  4891.         I:=L;
  4892.         
  4893.         {LOOP}
  4894. 300:
  4895. {
  4896. C               INVARIANT: MAXHEAP(L,U) EXCEPT PERHAPS
  4897. C                       BETWEEN I AND ITS (0,1 OR 2) CHILDREN
  4898. C
  4899. }
  4900.                 CHILD:=2*I;
  4901.  
  4902.                 IF CHILD > U  then goto 999;
  4903. {
  4904. C
  4905. C               IF C+1 <= U AND X(C+1) > X(C) THEN C=C+1
  4906. C
  4907. }
  4908.                 IF(CHILD+1 <= U) THEN
  4909.                 IF(GTX(CHILD+1,CHILD))THEN
  4910.                         CHILD:=CHILD+1;
  4911.  
  4912. {                
  4913. C
  4914. C               CHILD IS THE GREATEST CHILD OF I
  4915. C
  4916. C               IF X(I) >= X(CHILD) THEN RETURN
  4917. C
  4918. }
  4919.                 IF(GEX(I,CHILD)) then goto 999;
  4920.                 
  4921. {                
  4922. C
  4923. C               X(I) IS LESS THAN ITS GREATEST CHILD SO SWAP IT DOWNWARD
  4924. C               AND REPEAT LOOP
  4925. C
  4926. }
  4927.                 SWAPX(CHILD,I);
  4928.                 I:=CHILD;
  4929.                 GOTO 300;
  4930.         {END LOOP}
  4931. 999:{return}
  4932. END; {of proc siftdown}
  4933.  
  4934.  
  4935.        
  4936. begin {main body of sorttypes}
  4937.  
  4938.         for I:=N div 2 downto 1 do
  4939.         begin
  4940.        { echo(i);}
  4941.         SIFTDOWN(I,N);
  4942.         end;
  4943.  
  4944.         {echo(0);}
  4945.  
  4946.         for I:=N downto 2 do
  4947.         begin
  4948.           {  echo(i);}
  4949.                 SWAPX(1,I);
  4950.                 {echo(i);}
  4951.                 SIFTDOWN(1,I-1);
  4952.                { echo(i);}
  4953.          end;
  4954.  
  4955.  
  4956.  
  4957. END; {sorttypes}
  4958. {$S wascore}
  4959. function min(i,j:longint):longint;
  4960. begin
  4961. if i<j then min:=i else min:=j;
  4962. end;
  4963. function rotatelong(i:longint):longint;
  4964. {left circular shift by one bit}
  4965. const leftbitnum=0;
  4966.       rightbitnum=31;
  4967. var j:longint; 
  4968. begin
  4969. j:=BitShift(i,1);{left logical shift one}
  4970. if bittst(@i,leftbitnum) then
  4971.     begin
  4972.     {high bit set in i}
  4973.     bitset(@j,rightbitnum);
  4974.     end
  4975.   ;
  4976. rotatelong:=j;
  4977. end;
  4978.  
  4979. function checksumHdataOLD{(h:handle):integer};
  4980. {OLD version using the toolbox bit manipulation stuff
  4981.  and an intermediate buffer array (abount 10 times slower)}
  4982. {non-standard checksum for virus detection
  4983.  depends on all bits and can detect  transpositions}
  4984. {modified to increase non-linearity 3/29/88}
  4985. const mask=$00FFFFFF;
  4986.       blocklongsize=64;
  4987.       blockbytesize=256;{4x above}
  4988. var size:longint;
  4989.     p:ptr;
  4990.     p0:longint;
  4991.     sum:integer;
  4992.     lsum:longint;
  4993.     offset:longint;
  4994.     longxxxx:longint;
  4995.     j,kk:longint;
  4996.     work:array[1..blocklongsize] of longint;
  4997. begin
  4998. sum:=0;
  4999. lsum:=0;
  5000. longxxxx:=0;
  5001. if h<>nil then
  5002.   if (h^<>nil) then
  5003.      begin
  5004.      size:=GetHandleSize(h);
  5005.      offset:=0;
  5006.      while offset<(size-1) do
  5007.         begin
  5008.         
  5009.         for j:=1 to blocklongsize do work[j]:=0;
  5010.         p0:=bitand(mask,ord4(h^));        
  5011.         p:=pointer(p0+offset);
  5012.         kk:=min(longint(blockbytesize),size-offset);
  5013.         blockmove(p,@work,kk);{copy a block}
  5014.         for j:=1 to (kk+3) div 4 do {do for longints}
  5015.            begin
  5016.               lsum:=bitxor(lsum,work[j]);
  5017.               longxxxx:=longxxxx+checksumsaltinc;
  5018.               {lsum:=bitxor(lsum,longxxxx);}{3/29/88}
  5019.               lsum:=lsum+longxxxx;
  5020.               lsum:=rotatelong(lsum);
  5021.            end;
  5022.         offset:=offset+blockbytesize;
  5023.         end;
  5024.      sum:=loword(bitxor(longint(loword(lsum)),longint(hiword(lsum)) ));
  5025.      end;
  5026.      
  5027. checksumhdataOLD:=sum;
  5028.  
  5029. end;
  5030. {$S core}
  5031. function checksumHdata{(h:handle):integer};
  5032. {faster version using more stuff specific to turbo pascal 3/28/88}
  5033. {non-standard checksum for virus detection
  5034.  depends on all bits and can detect  transpositions}
  5035. {modified to use Turbo's inline code bit masking operators
  5036.  rather than the toolbox bitxor etc}
  5037. {modified to operate directly on the data in the handle
  5038.  rather than copying blocks into a buffer}
  5039. {modified to increase non-linearity 3/29/88}
  5040. const mask=$00FFFFFF;
  5041.       leftbitmask= $80000000;
  5042.       rightbitmask=$00000001;
  5043.       blocklongsize=8000;
  5044.       blockbytesize=32000;{4x above}
  5045. type workblocktype=array[1..blocklongsize] of longint;
  5046.      workptr=^workblocktype;
  5047. var size,realsize,sizeextra:longint;
  5048.     p:ptr;
  5049.     wrk:workptr;
  5050.     p0:longint;
  5051.     sum:integer;
  5052.     lsum:longint;
  5053.     offset:longint;
  5054.     longxxxx:longint;
  5055.     j,kk:longint;
  5056.     shwork:longint;
  5057.     last:longint;
  5058. begin
  5059. sum:=0;
  5060. lsum:=0;
  5061. longxxxx:=0;
  5062. if h<>nil then
  5063.   if (h^<>nil) then
  5064.      begin
  5065.      hlock(h);
  5066.      size:=GetHandleSize(h);
  5067.      realsize:=size;
  5068.      sizeextra:=size mod 4;
  5069.      size:=size-sizeextra;
  5070.      offset:=0;
  5071.      p0:=bitand(mask,ord4(h^));        
  5072.      while offset<(size-1) do
  5073.         begin
  5074.         p:=pointer(p0+offset);
  5075.         kk:=min(longint(blockbytesize),size-offset);
  5076.         wrk:=workptr(p);
  5077.         for j:=1 to (kk+3) div 4 do {do for longints}
  5078.            begin
  5079.               lsum:=(lsum xor wrk^[j]);
  5080.               longxxxx:=longxxxx+checksumsaltinc;
  5081.               {lsum:=(lsum xor longxxxx);}{3/29/88}
  5082.               lsum:=lsum+longxxxx;
  5083.  
  5084.               {simulate a left circular shift of one on lsum}
  5085.               shwork:=lsum shl 1;{left logical shift one}
  5086.               if (lsum and leftbitmask)<>0 then
  5087.                   begin
  5088.                   {high bit set in lsum}
  5089.                   shwork:=shwork or rightbitmask
  5090.                   end;
  5091.                lsum:=shwork;
  5092.                {end shift}
  5093.            end;
  5094.         offset:=offset+blockbytesize;
  5095.         end;
  5096.      if sizeextra<>0 then
  5097.      begin
  5098.      {special case for trailing bytes in last longword}
  5099.      wrk:=workptr(pointer(p0+size));
  5100.      last:=wrk^[1];
  5101.      case sizeextra of
  5102.      1:last:=last and $FF000000;
  5103.      2:last:=last and $FFFF0000;
  5104.      3:last:=last and $FFFFFF00;
  5105.      end;{case}
  5106.            begin{copy of code above except 1st line}
  5107.               lsum:=(lsum xor last);
  5108.               longxxxx:=longxxxx+checksumsaltinc;
  5109.               {lsum:=(lsum xor longxxxx);}{3/29/88}
  5110.               lsum:=lsum+longxxxx;
  5111.  
  5112.               {simulate a left circular shift of one on lsum}
  5113.               shwork:=lsum shl 1;{left logical shift one}
  5114.               if (lsum and leftbitmask)<>0 then
  5115.                   begin
  5116.                   {high bit set in lsum}
  5117.                   shwork:=shwork or rightbitmask
  5118.                   end;
  5119.                lsum:=shwork;
  5120.                {end shift}
  5121.            end;
  5122.      
  5123.      end;{sizeextra non-zero}
  5124.      sum:=loword(bitxor(longint(loword(lsum)),longint(hiword(lsum)) ));
  5125.      hunlock(h);
  5126.      end;
  5127.      
  5128. checksumhdata:=sum;
  5129. end;{function}
  5130.  
  5131. {$S core}
  5132. procedure check_all_types(filename:str255;
  5133.                     fnindex:integer;{index of file name - system folder checks only}
  5134.                     apindex:integer;{subscript of application - application checks only}
  5135.                     SYSTEMFOLDER:boolean);{true for system check, false for application check}
  5136. {This is the routine that checks all types in a given file}
  5137.  {called by check_a_file}
  5138. const
  5139.     keymousemask=62;{all mouse and key events}  
  5140.     applsalt=$1234;{to make application checksum over resources non-linear}                  
  5141. var 
  5142.     j:integer;
  5143.     tt:restype;
  5144.     ok:boolean;
  5145.     err:oserr;
  5146.     index:integer;
  5147.     itt:integer;
  5148.     CHECKIT:BOOLEAN;
  5149.     sl:safetype; 
  5150.     rhand:handle;
  5151.     rid:integer;
  5152.     rsize:longint;
  5153.     rattr:integer;
  5154.     rname:str255;
  5155.     aevent:EventRecord;
  5156.     volref:integer;
  5157. begin
  5158.    {dbashow;}
  5159.    {Open resource fork as a file and load the
  5160.      header info into my own data structures}
  5161.    
  5162.    if systemfolder then
  5163.       begin  
  5164.           sl:=filenamesafetylevel(filename);
  5165.       end
  5166.    else
  5167.       begin
  5168.           sl:=unknown;
  5169.       end;
  5170.    volref:=0;{look in default directory}
  5171.    err:=openpath(myRpath,filename,volref);
  5172.    
  5173.    if err=noerr then
  5174.    with myRpath do
  5175.    begin
  5176.       { ntypes number of resource types in the current resource file}
  5177.      { dbashow;}
  5178.      { wait_for_buttons(' start types',continuebut);}
  5179.       for j:=1 to ntypes do
  5180.       if setmytype(myRpath,j,tt) then
  5181.          begin
  5182.          {breakout on mouse,key events}
  5183.          if EventAvail(keymousemask,aevent) then DoEvent(true);
  5184.          {nrefs is count of this type}
  5185.            {poststatus(concat(concat('$',tt),'$'),resline);} 
  5186.             if showdebuginfo then poststatus(tt,resline); 
  5187.            
  5188.            if systemfolder then add_type(tt,unknown);{the detailed list of types 
  5189.                                                      is for the system folder only
  5190.                                                      except for "dangerous" types}
  5191.                                                      
  5192.            {decide what types should be checked}
  5193.            itt:=find_type(tt);
  5194.            CHECKIT:=(tt='INIT') or (tt='CODE');
  5195.            if itt<>0 then
  5196.                with rtypes[itt] do
  5197.                   begin
  5198.                     checkit:=safety>sl;{check resources above
  5199.                     safe or unknown depending on file,location}
  5200.                     if (not systemfolder) and (safety=dangerous) then
  5201.                         begin
  5202.                             {record dangerous types in applications}
  5203.                             ainfo^[apindex].flags:= 
  5204.                             ainfo^[apindex].flags or appldangermask
  5205.                         end;
  5206.                     if (systemfolder) or (safety=dangerous) then                   
  5207.                     begin
  5208.                     occurs:=occurs+nrefs;
  5209.                     if checkit then 
  5210.                         notsafecount:=notsafecount+nrefs;
  5211.                     end;
  5212.                   end;
  5213.  
  5214.            if checkit then
  5215.              begin
  5216.              for index:=1 to nrefs do 
  5217.                 begin
  5218.                   {copy resource data and get name and size}
  5219.                   if CopyResData(myRpath,index,
  5220.                          rid,rsize,rattr,rname) then
  5221.                   if systemfolder then
  5222.                     begin
  5223.                     {system folder check}
  5224.                      if rcount<maxinfo then
  5225.                         begin
  5226.                           rcount:=rcount+1; 
  5227.                           with rinfo^[rcount] do
  5228.                             begin
  5229.                               thetype:=tt;
  5230.                               theid:=rid;
  5231.                               filenameindex:=fnindex;
  5232.                               thesize:=rsize;
  5233.                               thename:=rname;
  5234.                               checksum:=0;{in case all else fails}
  5235.                               {dbashow;}
  5236.                               {compute checksums}
  5237.                               checksum:=checksumHdata(resdata);
  5238.                               checksumchecksum:=checksumchecksum+checksum;
  5239.                             end;{with}
  5240.                          end;{rcount<maxinfo/rhand<>nil}
  5241.                     end
  5242.                   else
  5243.                     begin
  5244.                       {application check}
  5245.                        with ainfo^[apindex] do
  5246.                          begin
  5247.                             if unsafecount<0 then unsafecount:=0;
  5248.                             unsafecount:=unsafecount+1;
  5249.                             checksize:=checksize+rsize;
  5250.                             {compute checksums}
  5251.                             checksum:=(checksum xor applsalt)+checksumHdata(resdata);
  5252.                          end;
  5253.                     end
  5254.                   {dbashow;}    
  5255.                   end;{for index}
  5256.               end;{if checkit}
  5257.          {dbashow;}     
  5258.          end;{for types/set ok}
  5259.    end;{if open ok/with mypath}
  5260.        
  5261. closepath(myRpath); 
  5262. {dbashow;}
  5263. end;{proc check_all_types}
  5264.  
  5265.  
  5266.  
  5267. procedure check_a_file{(index:integer)};
  5268.  
  5269. var
  5270.     i:integer;
  5271.     filename:str255;
  5272.     volref:integer;
  5273. begin
  5274. filename:=sysfiles[index];
  5275. postmem(memline);
  5276. check_all_types(filename,index,1,true);
  5277. end;
  5278. {$S appl}
  5279. procedure checksum_all_appl;
  5280. {go back and checksum applications
  5281.  from list in memory}
  5282.  var i:integer;
  5283.      oldvol:integer;
  5284.      ignore:oserr;
  5285.      percent:longint;
  5286.      pct:str255;
  5287.      err:oserr;
  5288.      vr:integer;
  5289. begin
  5290. if fastapplcheck then exit;{skip in fast mode}
  5291. ignore:=getvol(nil,oldvol);
  5292. set_default_blessed;
  5293. for i:=1 to acount do
  5294.     with ainfo^[i] do
  5295.   begin
  5296.      vr:=newvols[flags and applvolumemask].volrefnum;
  5297.      err:=setvol(nil,vr);
  5298.      if err=noerr then
  5299.         begin
  5300.           set_default_by_id(dirid);{set directory} 
  5301.           percent:=i*100;
  5302.           percent:=percent div acount;
  5303.           numtostring(percent,pct);
  5304.           pct:=concat(pct,'%');
  5305.           pct:=concat(concat(filename,' '),pct);
  5306.           poststatus(pct,fileline);
  5307.           check_all_types(filename,1,i,false);
  5308.         end;
  5309.   end;
  5310. clear_to_end(fileline);
  5311. ignore:=setvol(nil,oldvol);
  5312. end;{proc}
  5313. {$S core}
  5314. procedure summary;
  5315. {write summary to output file in same format as input file}
  5316. var i:integer;
  5317.     tab:string[1];
  5318.     now:longint;
  5319.     ndate,ntime:str255;
  5320. begin
  5321. if not writeoutputflag then exit;
  5322. {-----------------------------------}
  5323. tab:=chr(9);
  5324. poststatus('Writing System Summary Output',pathline);
  5325.  
  5326. {get time stamp}
  5327. getdatetime(now);
  5328. IUDateString(now,abbrevdate,ndate);
  5329. IUTImeString(now,false,ntime);
  5330. scsi_wait;
  5331. writeln(outfile,blessedpath,tab,ndate,tab,ntime,tab,titleversion);
  5332. scsi_wait;
  5333. writeln(outfile,bootblockchecksum);
  5334. scsi_wait;
  5335. writeln(outfile,checksumchecksum);{grand checksum}
  5336. scsi_wait;
  5337. writeln(outfile,rcount,tab,rtypes_count,tab,notsafecount);
  5338. write_end_flag('end header');
  5339. write_safekeys;
  5340. write_morechecks;
  5341. {end of "header"}
  5342. write_vols;{volumes list}
  5343. for i:=1 to rtypes_count do
  5344.     begin
  5345.     with rtypes[i] do
  5346.        begin
  5347.           scsi_wait;
  5348.           writeln(outfile,thetype,tab,ord(safety),tab,occurs);
  5349.        end;
  5350.     end;
  5351. write_end_flag('end types');
  5352. for i:=1 to rcount do
  5353.     with rinfo^[i] do
  5354.       begin
  5355.      scsi_wait;
  5356.       write(outfile,
  5357.       thetype:4,tab,theid:7,tab,thesize:10,tab,
  5358.       checksum:7,tab,thename,tab,sysfiles[(filenameindex and fnamemask)]);
  5359. if inputopen then
  5360.       begin
  5361.       if (filenameindex and exactmatchmask)=exactmatchmask then
  5362.            begin
  5363.            {normal}
  5364.            writeln(outfile);
  5365.            end
  5366.       else
  5367.          begin
  5368.            if (filenameindex and idmatchmask)=idmatchmask then
  5369.               begin
  5370.               writeln(outfile,tab,'changed??');
  5371.               end
  5372.            else
  5373.               begin
  5374.               writeln(outfile,tab,'new??');
  5375.               end
  5376.          end;
  5377.       end
  5378.       else
  5379.            begin
  5380.            {normal no input file}
  5381.            writeln(outfile)
  5382.            end;
  5383.       
  5384.       end;{for}
  5385. write_end_flag('end res checks');
  5386. end;{proc summary}
  5387.  
  5388. {$S core}
  5389. procedure get_set_blessed;
  5390. {get the blessed folder and make it the default and
  5391. build it's pathname}
  5392. var
  5393.     volume:integer;
  5394.     name:str255;
  5395. begin
  5396. get_blessed;
  5397. set_default_blessed;
  5398. folder_info(0,blessed,volume,name,blessedpath);
  5399. end;{proc}
  5400.  
  5401.  
  5402. procedure docheck;
  5403. {top level procedure for the section of the program that
  5404. does the work. Note there is no main event loop in the usual sense
  5405. I call DoEvent from all over, mostly when posting progress
  5406. messages on the screen and loop till I get a null event}
  5407.  
  5408. begin
  5409. rcount:=0;
  5410. acount:=0;
  5411. {display the system folder}
  5412. poststatus(blessedpath,pathline);
  5413.  
  5414. {checksum the boot blocks}
  5415. poststatus('Checking boot blocks',fileline);
  5416. bootblockchecksum:=checksum_boot_blocks;
  5417.  
  5418. {checksum all resources in the system folder}
  5419. enumeratecatalog(blessed);
  5420. poststatus('Sort Resources',fileline);
  5421. sortresources(rinfo,rcount);
  5422. clear_to_end(fileline);
  5423. {compare over-all checksums}
  5424. if inputopen then
  5425.    begin
  5426.    if bootblockchecksum<>oldbootblockchecksum then
  5427.     begin
  5428.       Wait_for_buttons('The boot blocks appear to have changed',continuebut);
  5429.     end;
  5430.    if checksumchecksum<>oldchecksumchecksum then
  5431.     begin
  5432.       Wait_for_buttons('The over-all checksum of resources has changed',continuebut);
  5433.     end;
  5434.    end;{if inputopen}
  5435. detail_resource_check;
  5436. show_detail_changes;   
  5437. {write summary output file}    
  5438. {summary;}
  5439.  
  5440. poststatus('',resline);
  5441. (*close_and_flush(outfile,outputopen);*)
  5442. end;{docheck}
  5443.  
  5444. {$S events}
  5445. procedure dokeypress; 
  5446. {key events
  5447.   ignore modifier keys
  5448.   Q is quit
  5449.   . is halt
  5450.   F is Full Check on start up
  5451.   A is system only check on startup
  5452.   * is debugger
  5453.   Y N are replies to questions Yes and No
  5454.   return is default button if any}
  5455. var
  5456.     whichWindow :   WindowPtr;
  5457.     chcode:integer;
  5458.     ch:str255;
  5459.     menuchoice:longint;
  5460.     
  5461. begin
  5462.    with theevent do
  5463.         begin
  5464.            chcode:=bitand(message,CharCodeMask);
  5465.            ch:=chr(chcode);
  5466.            uprstring(ch,true);   
  5467.            if (ch='Q') then
  5468.               begin
  5469.                quitting:=true;
  5470.               end
  5471.            else if (ch='*') then
  5472.               begin
  5473.                 showdebuginfo:=true;
  5474.                 debugger;{Macsbug}
  5475.               end
  5476.            else if (ch='Y') and (not askanswered) then
  5477.               begin
  5478.                 dobutton(yesbut);
  5479.               end
  5480.            else if (ch='N') and (not askanswered) then
  5481.               begin
  5482.                 dobutton(nobut);
  5483.               end
  5484.            else if (ch='A') and (not askanswered) then
  5485.               begin
  5486.                 dobutton(sysonlybut);
  5487.               end
  5488.            else if (ch='F') and (not askanswered) then
  5489.               begin
  5490.                 dobutton(fullbut);
  5491.               end
  5492.            else if (ch='.') then
  5493.               begin
  5494.                 halt;{emergency exit}
  5495.               end
  5496.             else if (chcode=13) and (not askanswered) then
  5497.               begin
  5498.                  dobutton(defaultbutton);
  5499.               end
  5500.             else
  5501.               begin
  5502.                 sysbeep(1);
  5503.               end
  5504.          end;{with}
  5505.              
  5506. end;
  5507.  
  5508. procedure drawlong(l:longint);
  5509. var s:str255;
  5510. begin
  5511.    NumtoString(l,s);
  5512.    DrawString(concat(s,' '));
  5513. end;
  5514.  
  5515. procedure drawbuttons;
  5516. {draw buttons and frame around the default}
  5517. var
  5518.    saveport:grafptr;
  5519.    wait,endtick:longint;
  5520.    h,v:integer;
  5521.    ii:integer;
  5522.    rr:rect;
  5523. begin
  5524. getport(saveport);
  5525. setport(mainwindow);
  5526. (*
  5527. {zap invisibles}
  5528. for ii:=1 to mbutton do
  5529.     if buttons[ii]^^.contrlVis<>255 then
  5530.     begin
  5531.      rr:=buttonrects[ii];
  5532.      insetrect(rr,-4,-4);
  5533.      eraseroundrect(rr,22,22);
  5534.     end;
  5535. *)
  5536. {draw visibles}
  5537. for ii:=1 to mbutton do
  5538.     if buttons[ii]^^.contrlVis=255 then
  5539.     begin
  5540.      rr:=buttonrects[ii];
  5541.      if (ii=defaultbutton) then forecolor(blackcolor) else forecolor(whitecolor);
  5542.      insetrect(rr,-4,-4);
  5543.      pensize(2,2);
  5544.      frameroundrect(rr,22,22);
  5545.      pensize(1,1);
  5546.     end;
  5547. forecolor(blackcolor);
  5548. drawcontrols(mainwindow);
  5549. setport(saveport);
  5550. end;
  5551.  
  5552. procedure showstatus;
  5553. {redraw the status message display and buttons}
  5554. var
  5555.    saveport:grafptr;
  5556.    wait,endtick:longint;
  5557.    h,v:integer;
  5558. begin
  5559. getport(saveport);
  5560. setport(mainwindow);
  5561. EraseRect(mainwindow^.portrect);
  5562. teupdate(textbounds,statustext);
  5563. framerect(textframe);
  5564. drawbuttons;    
  5565. setport(saveport);
  5566. end;
  5567.  
  5568. procedure  doshutdown;
  5569. {flush drives and do a system shutdown}
  5570. const maxdrive=32;
  5571. var theerr:oserr;
  5572.     volref:integer;
  5573.     freebytes:longint;
  5574.     drive:integer;
  5575.     vname:str255;
  5576. begin
  5577. flushevents(everyevent,0);  {clear out event queue}
  5578. {flush default volume}
  5579. theerr:=FlushVol(nil,0);
  5580. {loop over small drive numbers to try and flush the rest}
  5581.  
  5582. for drive:=1 to maxdrive do begin
  5583.    theerr:=getvinfo(drive,@vname,volref,freebytes);
  5584.    if theerr=noerr then
  5585.       begin
  5586.          {writeln(theerr,' ',vname,' ',drive);}
  5587.          theerr:=FlushVol(nil,drive);
  5588.          {writeln(theerr);}
  5589.          theerr:=eject(nil,drive);
  5590.          {writeln(theerr);}
  5591.       end
  5592.    end;
  5593.    
  5594. ShutDwnPower;
  5595. end;{doshutdown}
  5596.  
  5597. procedure dobutton{(whichbutton:integer)};
  5598. {actions for buttons and default buttons}
  5599. begin
  5600. case whichbutton of
  5601.   nodefaultbut:{do nothing};
  5602.   continuebut:
  5603.                begin
  5604.                    {continue}
  5605.                  askanswered:=true;
  5606.                 end;
  5607.   haltbut:
  5608.                begin 
  5609.                  {halt}              
  5610.                  close_all_and_halt(true);
  5611.                  askanswered:=true;
  5612.                end;
  5613.    skipitbut:
  5614.                begin 
  5615.                  {same as halt}              
  5616.                  close_all_and_halt(true);
  5617.                  askanswered:=true;
  5618.                end;
  5619.  shutdownbut:
  5620.                begin 
  5621.                  {Shutdown}              
  5622.                  doshutdown;
  5623.                  askanswered:=true;
  5624.                end;
  5625.   yesbut:
  5626.                begin 
  5627.                  {yes}              
  5628.                  askanswer:=true;
  5629.                  askanswered:=true;
  5630.                end;
  5631.   nobut:
  5632.                begin 
  5633.                  {no}              
  5634.                  askanswer:=false;
  5635.                  askanswered:=true;
  5636.                end;
  5637.   shortbut:
  5638.                begin 
  5639.                  {short check} 
  5640.                  fastapplcheck:=true;
  5641.                  skipapplcheck:=false;             
  5642.                  askanswered:=true;
  5643.                end;
  5644.   sysonlybut:
  5645.                begin 
  5646.                  {short check} 
  5647.                  fastapplcheck:=true;
  5648.                  skipapplcheck:=true;             
  5649.                  askanswered:=true;
  5650.                end;
  5651.   fullbut:
  5652.                begin 
  5653.                  {full check}              
  5654.                  fastapplcheck:=false;
  5655.                  skipapplcheck:=false;            
  5656.                  askanswered:=true;
  5657.                end;
  5658.     end;{case}
  5659.  
  5660. end;{proc}
  5661. procedure DoControls(whichwindow:windowptr;local:point);
  5662. {process hits on controls}
  5663. label 88;
  5664. var whichcontrol:controlhandle;
  5665.     part,tresult:integer;
  5666.     wait,endwait:longint;
  5667.     jbut:integer;
  5668. begin
  5669. setport(whichwindow);
  5670. part:=findcontrol(local,whichwindow,whichcontrol);
  5671. if (part<>0) and (whichcontrol<>nil) then
  5672.    begin
  5673.    HiLiteControl(whichcontrol,part);{highlight part}
  5674.    drawbuttons;
  5675.    wait:=30;
  5676.    delay(wait,endwait);
  5677.    case part of
  5678.    InButton:
  5679.       begin
  5680.         if trackcontrol(whichcontrol,local,nil)<>0 then
  5681.           begin
  5682.           for jbut:=1 to mbutton do
  5683.             if whichcontrol=buttons[jbut] then
  5684.                begin
  5685.                  dobutton(jbut);
  5686.                  goto 88;
  5687.                 end;
  5688.           end;
  5689.       end;      
  5690.    end;{case}
  5691.    88:
  5692.     HiLiteControl(whichcontrol,0);{unhighlight}
  5693.     drawbuttons;
  5694.    end;
  5695.  
  5696. end;
  5697.  
  5698. procedure doclick;
  5699. {process mouse down events}
  5700. var whichwindow:windowptr;
  5701.     global,local:point;
  5702.     saveport:grafptr;
  5703.     inwhat:integer;
  5704. begin
  5705. getport(saveport);
  5706.     global:=theEvent.where;
  5707.     inwhat:=findwindow(global,whichwindow);
  5708.     if whichwindow<>nil then
  5709.         begin
  5710.         setport(whichwindow); 
  5711.         end;
  5712.      local:=global;
  5713.      globaltolocal(local);
  5714.      case inwhat of
  5715.          indesk:;
  5716.          inmenubar:;
  5717.          insyswindow:;
  5718.          incontent:
  5719.             begin
  5720.             DoControls(whichwindow,local);
  5721.             end;
  5722.          indrag:;
  5723.          ingrow:;
  5724.          ingoaway:quitting:=true;
  5725.          end;{case}
  5726.  
  5727. setport(saveport);
  5728. end;
  5729.  
  5730. procedure DoNull(dontloop:boolean);
  5731. {background/idle Event processing}
  5732. begin
  5733. if Quitting and (TheEvent.what = NullEvent) then
  5734.      begin
  5735.         finished:=true;
  5736.         close_all_and_halt(true);
  5737.      end {if}
  5738. else if (theevent.what =nullevent)  then
  5739.      begin
  5740.        
  5741.        {showstatus;}
  5742.         if lowmemoryGZflag then low_memory_halt;
  5743.      end;
  5744.  
  5745. end;{DoNull}
  5746. procedure eventmonitor;
  5747. {for debugging}
  5748. var s1,s2,s3:str255;
  5749. begin
  5750. NumToString(longint(theevent.what),s1);
  5751. case theevent.what of
  5752.     NullEvent:s1:='Null';
  5753.     MouseDown:s1:='MouseDn';
  5754.     MouseUp:s1:='MouseUp';
  5755.     UpdateEvt:s1:='Update';
  5756.     ActivateEvt:begin
  5757.                  if odd(theevent.modifiers) then s1:='Act' else s1:='DeAct';
  5758.                 end;
  5759.     otherwise
  5760.        {pass s1 as is}
  5761. end;{case}
  5762. numtostring(longint(theevent.modifiers),s2);
  5763. s3:=concat(s1, ' ',s2);
  5764. {debug_mess(s3);}
  5765. end;
  5766. procedure doevent{(dontloop:boolean)};
  5767. {modified event loop for calling with other routines
  5768.  process events till it sees a null event if dontloop is false}
  5769. var 
  5770.     looplimiter:integer;
  5771.     Eventstatus:boolean;{indicates if we should handle this event}
  5772.     oureventmask:integer;
  5773.     savemouse:EventRecord;
  5774.     foundin:integer;
  5775.  begin
  5776.  looplimiter:=0;
  5777.  repeat
  5778.        looplimiter:=looplimiter+1;
  5779.        oureventmask:=EveryEvent;
  5780.        begin
  5781.       systemtask;
  5782.       EventStatus:=GetNextEvent(oureventmask,TheEvent);
  5783.       end;
  5784.       
  5785.     {Event Processing:}
  5786.         if EventStatus then   
  5787.              case TheEvent.what of
  5788.                 MouseDown:
  5789.                     begin
  5790.                    {use mousedown to test option key}
  5791.                    optionkeyflag:=optionkeyflag or
  5792.                        (bitand(theevent.modifiers,optionkey)<>0);
  5793.                     DoClick;
  5794.                     end;
  5795.                 MouseUp:
  5796.                    begin
  5797.                    end;
  5798.                 UpdateEvt:
  5799.                    begin
  5800.                    beginupdate(mainwindow);
  5801.                    drawbuttons;
  5802.                    showstatus;
  5803.                    endupdate(mainwindow);
  5804.                    end;
  5805.                 ActivateEvt:
  5806.                    begin
  5807.                    {use activate to test option key}
  5808.                    optionkeyflag:=optionkeyflag or
  5809.                        (bitand(theevent.modifiers,optionkey)<>0);
  5810.                    end;
  5811.                 KeyDown,AutoKey:
  5812.                    begin
  5813.                    Dokeypress;
  5814.                    end;
  5815.                 otherwise
  5816.               end{case}
  5817.            {nullevents:}
  5818.            else if (theevent.what =nullevent) then
  5819.               DoNull(dontloop);
  5820. until((theevent.what=nullevent) or (dontloop) or (looplimiter>20))
  5821.  
  5822. end; {of proc DoEvent}
  5823. {$S startup}
  5824. procedure centerit(var rr:rect;height:integer;width:integer);
  5825. {center a rectangle on the screen}
  5826. var at:point;
  5827. begin
  5828. rr:=screenbits.bounds;
  5829. insetrect(rr,40,40);
  5830. with at do
  5831.   begin
  5832.   with screenbits.bounds do
  5833.     begin
  5834.       v:=(top+bottom) div 2;
  5835.       h:=(left+right) div 2;
  5836.     end;
  5837.   with rr do
  5838.     begin
  5839.       top:=v-(height div 2);
  5840.       bottom:=v+(height div 2);
  5841.       left:=h-(width div 2);
  5842.       right:=h+(width div 2);
  5843.     end; 
  5844.   end;
  5845. end;{proc centerit}
  5846.  
  5847. procedure mytextsetup;
  5848. {set up textedit record to display status information}
  5849. var i:integer;
  5850.     ll:integer;
  5851. begin
  5852.  
  5853. {define rect for textedit record for posting messages}
  5854. textbounds:=wbounds;
  5855. globaltolocal(textbounds.topleft);
  5856. globaltolocal(textbounds.botright);
  5857. insetrect(textbounds,10,10);
  5858. textbounds.bottom:=textbounds.bottom-60;
  5859. textframe:=textbounds;
  5860. Insetrect(textframe,-1,-1);
  5861. framerect(textframe);
  5862.  
  5863. {create TE Record}
  5864. statustext:=TENew(textbounds,textbounds);
  5865. {set to centered justification}
  5866. TeSetJust(tejustcenter,statustext);
  5867. {set to wider spacing}
  5868. with statustext^^ do 
  5869.    begin
  5870.      {ll:=lineHeight div 2;}
  5871.      ll:=5;
  5872.      lineheight:=lineheight+ll;
  5873.      fontascent:=fontascent+ll;   
  5874.    end;
  5875. tecaltext(statustext);   
  5876. {insert empty lines}
  5877. for i:=1 to mstatus+1 do replaceline(' ',i);
  5878. {put in startup info}
  5879. Replaceline(concat('Startup System Check ',TitleVersion),titleline);
  5880. ReplaceLine('by Albert Lunde, Northwestern University  Copyright © 1988'
  5881. ,byline);
  5882. replaceline(startversion,fileline);
  5883. {set inactive to hide insertion point}
  5884. tedeactivate(statustext);
  5885. framerect(textframe);
  5886. end;
  5887.  
  5888. procedure initialize;
  5889.  
  5890. var i:integer;
  5891.     ignore:oserr;
  5892. begin
  5893. rinfo:=nil;
  5894. ainfo:=nil;
  5895. rcount:=0;
  5896. acount:=0;
  5897. currentvolumesubscript:=0;
  5898. showdebuginfo:=false;
  5899. scsi_wait_count:=scsi_wait_limit div 2;
  5900. defaultbutton:=startupdefaultbutton;
  5901. askanswered:=false;
  5902. checksumchecksum:=0;
  5903. fastapplcheck:=true;
  5904. skipapplcheck:=false;
  5905. finished:=false;
  5906. quitting:=false;
  5907. mainwindow:=nil;
  5908. inputopen:=false;
  5909. outputopen:=false;
  5910. optionkeyflag:=false;
  5911. notsafecount:=0;
  5912.  
  5913. MaxApplZone;
  5914. MoreMasters;
  5915. MoreMasters;
  5916. MoreMasters;
  5917. MoreMasters;
  5918. FlushEvents(everyevent,0);
  5919. InitGraf(@thePort);
  5920. InitFonts;
  5921. InitWindows;
  5922. InitCursor;
  5923. TEInit;
  5924. for i:=1 to dbamax do dbarray[i]:=0;
  5925. ignore:=getvol(nil,startupwd);
  5926. setup_mygrowzone;{memory management}
  5927. setupmydebug;
  5928. {setup mainwindow centered on the screen}
  5929. centerit(wbounds,260,470);
  5930.  
  5931. mainwindow:=NewWindow(nil,wbounds,'Startup System Check',true,
  5932.         dboxProc,pointer(-1),false,0);
  5933. setport(mainwindow);
  5934. textfont(0);
  5935.  
  5936. mytextsetup;
  5937. initmypath(myRpath);
  5938. end;
  5939.  
  5940. procedure setup_buttons;
  5941. var i:integer;
  5942.     h,v:integer;
  5943.     cr,cmd:string[1];
  5944.     tag:str255;
  5945. begin
  5946. h:=12;
  5947. v:=210;
  5948. cr:=chr(13);
  5949. cmd:=chr(17);
  5950. for i:=1 to shutdownbut do
  5951.    with buttonrects[i] do
  5952.       begin
  5953.         top:=v;
  5954.         left:=h;
  5955.         right:=h+80;
  5956.         bottom:=v+38;
  5957.         h:=h+91;
  5958.       end;
  5959. h:=12;
  5960. v:=210;
  5961. for i:=shutdownbut+1 to mbutton do
  5962.    with buttonrects[i] do
  5963.       begin
  5964.         top:=v;
  5965.         left:=h;
  5966.         right:=h+80;
  5967.         bottom:=v+38;
  5968.         h:=h+91;
  5969.       end;
  5970.      
  5971. buttons[continuebut]:=NewControl(mainwindow,buttonrects[continuebut],
  5972. 'Continue',false,0,0,1,PushButProc,0);
  5973.  
  5974. buttons[yesbut]:=NewControl(mainwindow,buttonrects[yesbut],'Yes',false,
  5975.              0,0,1,PushButProc,0);
  5976. tag:=concat(concat(concat('Halt',cr),cmd),'Q');
  5977. buttons[haltbut]:=NewControl(mainwindow,buttonrects[haltbut],tag,false,
  5978.              0,0,1,PushButProc,0);
  5979. buttons[nobut]:=NewControl(mainwindow,buttonrects[nobut],'No',false,
  5980.              0,0,1,PushButProc,0);
  5981. tag:=concat(concat('Shut',cr),'Down');
  5982. buttons[shutdownbut]:=NewControl(mainwindow,buttonrects[shutdownbut],tag,true,
  5983.              0,0,1,PushButProc,0);
  5984. tag:=concat(concat(concat(concat('System   ',cr),'   Only  '),cmd),'A');
  5985. buttons[sysonlybut]:=NewControl(mainwindow,buttonrects[sysonlybut],tag,true,
  5986.              0,0,1,PushButProc,0);
  5987. tag:=concat(concat('Application',cr),'Scan');
  5988. buttons[shortbut]:=NewControl(mainwindow,buttonrects[shortbut],tag,true,
  5989.              0,0,1,PushButProc,0);
  5990. tag:=concat(concat('Full    ',cr),concat(concat('Check ',cmd),'F'));
  5991. buttons[Fullbut]:=NewControl(mainwindow,buttonrects[fullbut],tag,true,
  5992.              0,0,1,PushButProc,0);
  5993. tag:=concat(concat(concat('Skip It',cr),cmd),'Q');
  5994. buttons[skipitbut]:=NewControl(mainwindow,buttonrects[skipitbut],tag,true,
  5995.              0,0,1,PushButProc,0);
  5996.  
  5997. end;{proc setup_buttons}
  5998. procedure in_progress_buttons;
  5999. begin
  6000. HiliteControl(buttons[continuebut],255);{make inactive but visible}
  6001. HideControl(buttons[sysonlybut]);
  6002. HideControl(buttons[shortbut]);
  6003. HideControl(buttons[fullbut]);
  6004. HideControl(buttons[skipitbut]);
  6005. ShowControl(buttons[continuebut]);
  6006. ShowControl(buttons[haltbut]);
  6007. ShowControl(buttons[shutdownbut]);
  6008. doevent(true);
  6009. end;
  6010. {$S          }
  6011. begin
  6012.  
  6013. initialize;
  6014. kill_nil;
  6015. setup_buttons;
  6016. allocate_big_memory;
  6017. start_types;
  6018. start_safekey;
  6019. doEvent(true);
  6020.  
  6021. HFSwarning;{quit if no HFS}
  6022.  
  6023. get_set_blessed;{set default to system folder}
  6024. Poststatus(blessedpath,pathline);
  6025. postmem(memline);
  6026.  
  6027. wait_for_start(
  6028. 'This will take a minute or two to check the system folder and applications.'
  6029. ,startupdelay); 
  6030. in_progress_buttons;
  6031. WriteOutputFlag:=optionKeyFlag;{get state of option key from 
  6032.                                 button click or activate}
  6033. {look for input file}
  6034. open_input;
  6035. {ask about output file if option key down or input file not found}
  6036. if WriteOutputFlag or InputNotdefault then
  6037.    begin
  6038.    if ask('Do you want to write a new summary output file?',yesbut) then 
  6039.       begin
  6040.        open_output;
  6041.        if writeoutputflag and fastapplcheck then
  6042.           fastapplcheck:=not Ask('Do you want a full checksum of applications',yesbut);    
  6043.       end
  6044.    else
  6045.       begin
  6046.       writeoutputflag:=false;
  6047.       end;
  6048.     end
  6049.  else
  6050.     begin
  6051.     poststatus('(start with option key to write output file)',pathline);
  6052.     end;
  6053. read_input_header;
  6054. dovols;
  6055. readoklist;
  6056. {debugger;}
  6057. docheck;
  6058. if outputopen then skipapplcheck:=false;
  6059. if not skipapplcheck then docheck_applications;
  6060. summary;
  6061. if not skipapplcheck then 
  6062.      APPLsummary
  6063. else
  6064.      copyAPPLSummary;
  6065.      
  6066. PostStatus('DONE',fileline);
  6067. sysbeep(1);
  6068. close_all_and_halt(false);
  6069. end.{color3d main}
  6070.